fix(game): correct every claw release velocity

Interpret the claw's 3800-unit release scalar per 10 ms physics substep rather
than per 30 ms animation frame. This removes a factor-of-three slowdown from all
four random terminals and restores terminal 18 to raw `(0,1000)`.

Add exact assertions for positions and raw velocities of terminals 1, 6, 7,
and 18, backed by repeated live original captures through release and the first
three gravity substeps.

Test Plan:
- `cargo test --all-targets` -- 48 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- all four live original terminal trajectories matched
- `git diff --cached --check` -- passed
This commit is contained in:
2026-08-22 21:46:07 +02:00
parent 4b56a23fb4
commit 27749cb5f8
3 changed files with 37 additions and 11 deletions
+29 -10
View File
@@ -29,7 +29,7 @@ const CLAW_TRIGGER_RADIUS: f32 = 19.0;
// deterministic on modern machines.
const CLAW_FRAME_SECONDS: f32 = 0.030;
const CLAW_TERMINAL_FRAMES: [u8; 4] = [1, 6, 7, 18];
const ORIGINAL_BALL_SPEED_PER_SECOND: f32 = 3.8 / CLAW_FRAME_SECONDS;
const ORIGINAL_BALL_SPEED_PER_SECOND: f32 = 380.0;
#[derive(Clone, Copy, Debug, Default)]
pub struct Controls {
@@ -925,7 +925,7 @@ fn claw_release(frame: u8) -> (Vec2, Vec2) {
7 => (vec2(275.0, 97.0), vec2(-0.31, 0.70)),
8 => (vec2(278.0, 98.0), vec2(-0.20, 0.80)),
9 => (vec2(282.0, 101.0), vec2(-0.10, 0.90)),
18 => return (vec2(325.0, 92.0), vec2(0.0, 1.0 / CLAW_FRAME_SECONDS)),
18 => return (vec2(325.0, 92.0), vec2(0.0, 100.0)),
_ => unreachable!("the original claw only releases from a terminal frame"),
};
(position, direction * ORIGINAL_BALL_SPEED_PER_SECOND)
@@ -1421,8 +1421,8 @@ mod tests {
assert_eq!(game.claw.bank, ClawSpriteBank::Opening);
assert!(!game.claw.ball_suspended);
assert_eq!(game.ball.position, vec2(270.0, 94.0));
assert!((game.ball.velocity.x - -50.666_668).abs() < 0.001);
assert!((game.ball.velocity.y - 76.0).abs() < 0.001);
assert!((game.ball.velocity.x - -152.0).abs() < 0.001);
assert!((game.ball.velocity.y - 228.0).abs() < 0.001);
for expected_frame in [7, 8, 9, 10] {
game.update_claw(CLAW_FRAME_SECONDS, &mut events);
@@ -1553,13 +1553,32 @@ mod tests {
#[test]
fn claw_release_table_decodes_the_original_thousandth_pixel_coordinates() {
for (frame, expected) in [
(1, vec2(258.0, 78.0)),
(6, vec2(270.0, 94.0)),
(7, vec2(275.0, 97.0)),
(18, vec2(325.0, 92.0)),
for (frame, expected_position, expected_velocity) in [
(1, vec2(258.0, 78.0), MilliVec { x: -2_280, y: 0 }),
(
6,
vec2(270.0, 94.0),
MilliVec {
x: -1_520,
y: 2_280,
},
),
(
7,
vec2(275.0, 97.0),
MilliVec {
x: -1_178,
y: 2_660,
},
),
(18, vec2(325.0, 92.0), MilliVec { x: 0, y: 1_000 }),
] {
assert_eq!(claw_release(frame).0, expected);
let (position, velocity) = claw_release(frame);
assert_eq!(position, expected_position);
assert_eq!(
MilliVec::from_velocity_per_second(velocity),
expected_velocity
);
}
}