fix(physics): restore upward flipper transfer
Apply flipper movement after the physics substep, matching the original timer order. Replace the weak generic tangent kick with the recovered swept-body position transfer and pre-clamp velocity curve fitted from three live left-edge probes, then mirror that local transform for the right flipper. The x=125 probe now reaches `(138.182,355)` with raw velocity within two units of `(2217,-5889)`. Keep downstroke interaction listed as pending rather than claiming the upward fit is the complete raw routine. Test Plan: - `cargo test --all-targets` -- 43 passed - `cargo clippy --all-targets -- -D warnings` -- passed - `cargo build --profile production` -- passed - three live left-flipper transfer probes matched within documented tolerance - `git diff --cached --check` -- passed
This commit is contained in:
+24
-17
@@ -9,7 +9,6 @@ use crate::{
|
||||
use macroquad::prelude::{Rect, Vec2, vec2};
|
||||
|
||||
const FLIPPER_CONTACT_RADIUS: f32 = 9.0;
|
||||
const FLIPPER_EDGE_KICK: f32 = 118.0;
|
||||
const LEFT_FLIPPER_PIVOT: Vec2 = Vec2::new(103.0, 397.0);
|
||||
const LEFT_FLIPPER_REST_TIP: Vec2 = Vec2::new(134.0, 419.0);
|
||||
const LEFT_FLIPPER_RAISED_TIP: Vec2 = Vec2::new(133.0, 377.0);
|
||||
@@ -392,8 +391,6 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
|
||||
self.apply_flipper_kicks();
|
||||
|
||||
if self.ball.in_launcher {
|
||||
self.ball.position = LAUNCHER_POSITION;
|
||||
self.ball.velocity = Vec2::ZERO;
|
||||
@@ -498,6 +495,8 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
|
||||
self.apply_flipper_kicks();
|
||||
|
||||
if self.ball.position.y > 470.0 {
|
||||
// Only malformed/out-of-table states reach this guard; the real
|
||||
// drain is collision object 2 at y=455.
|
||||
@@ -645,20 +644,20 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
|
||||
for (should_kick, pivot, rest_tip, raised_tip, clockwise) in [
|
||||
for (should_kick, pivot, rest_tip, raised_tip, horizontal_sign) in [
|
||||
(
|
||||
pending[0],
|
||||
LEFT_FLIPPER_PIVOT,
|
||||
LEFT_FLIPPER_REST_TIP,
|
||||
LEFT_FLIPPER_RAISED_TIP,
|
||||
true,
|
||||
1.0,
|
||||
),
|
||||
(
|
||||
pending[1],
|
||||
RIGHT_FLIPPER_PIVOT,
|
||||
RIGHT_FLIPPER_REST_TIP,
|
||||
RIGHT_FLIPPER_RAISED_TIP,
|
||||
false,
|
||||
-1.0,
|
||||
),
|
||||
] {
|
||||
if !should_kick
|
||||
@@ -666,13 +665,19 @@ impl Game {
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let radial = (self.ball.position - pivot).normalize_or_zero();
|
||||
let tangent = if clockwise {
|
||||
vec2(radial.y, -radial.x)
|
||||
} else {
|
||||
vec2(-radial.y, radial.x)
|
||||
};
|
||||
self.ball.velocity += tangent * FLIPPER_EDGE_KICK;
|
||||
let local_x = (self.ball.position.x - pivot.x) * horizontal_sign;
|
||||
let local_y = self.ball.position.y - pivot.y;
|
||||
let horizontal_displacement =
|
||||
0.002_12 * local_x.powi(2) - 0.125_68 * local_x + 14.921 - (local_y + 7.0);
|
||||
let vertical_displacement = -(local_x + 13.0);
|
||||
let velocity_scale = -0.026 * local_x.powi(2) + 3.614 * local_x + 101.327;
|
||||
let displacement = vec2(
|
||||
horizontal_displacement * horizontal_sign,
|
||||
vertical_displacement,
|
||||
);
|
||||
self.ball.position += displacement;
|
||||
let velocity_milli = MilliVec::from_millipixels(displacement * velocity_scale);
|
||||
self.ball.velocity = velocity_milli.to_velocity_per_second();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1161,17 +1166,19 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raising_flipper_applies_one_swept_tangential_kick() {
|
||||
fn raising_flipper_matches_the_live_swept_transfer_probe() {
|
||||
let mut game = Game::new(1);
|
||||
game.ball.in_launcher = false;
|
||||
game.ball.position = (LEFT_FLIPPER_PIVOT + LEFT_FLIPPER_REST_TIP) * 0.5;
|
||||
game.ball.position = vec2(125.0, 390.0);
|
||||
game.ball.velocity = Vec2::ZERO;
|
||||
game.pending_flipper_kicks[0] = true;
|
||||
|
||||
game.apply_flipper_kicks();
|
||||
let velocity_after_edge = game.ball.velocity;
|
||||
assert!((velocity_after_edge.length() - FLIPPER_EDGE_KICK).abs() < 0.001);
|
||||
assert!(velocity_after_edge.y < 0.0);
|
||||
assert!(game.ball.position.distance(vec2(138.182, 355.0)) < 0.002);
|
||||
let raw_velocity = MilliVec::from_velocity_per_second(velocity_after_edge);
|
||||
assert!((raw_velocity.x - 2_217).abs() <= 2);
|
||||
assert!((raw_velocity.y - -5_889).abs() <= 2);
|
||||
assert_eq!(game.pending_flipper_kicks, [false, false]);
|
||||
|
||||
game.apply_flipper_kicks();
|
||||
|
||||
@@ -29,6 +29,13 @@ impl MilliVec {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_millipixels(value: Vec2) -> Self {
|
||||
Self {
|
||||
x: value.x.round() as i32,
|
||||
y: value.y.round() as i32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_position(self) -> Vec2 {
|
||||
vec2(self.x as f32 / 1_000.0, self.y as f32 / 1_000.0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user