fix(physics): restore fixed-point rail movement
Replace the guessed variable-rate floating integration with the original 10 ms millipixel step, 15-unit gravity, and 3800-unit speed limit. Restore every registered type-2 Real48 response pair, one-sided start-to-end orientation, and the original normal/tangent velocity transform validated against live shooter wall traces. Restore repeat-driven plunger charging and its release-time randomized clamp. The shooter route now follows table orientation instead of coordinate-specific gate exceptions. Remove the synthetic ball-search impulse so future stuck balls remain visible reconstruction failures, and include collision ids in deterministic traces for differential work on circles and triggers. Test Plan: - `cargo test --all-targets` -- 42 passed - `cargo clippy --all-targets -- -D warnings` -- passed - `cargo build --profile production` -- passed - charged launcher scenario cleared the shooter lane -- passed - all recovered rails accepted front-side and rejected back-side probes - `git diff --cached --check` -- passed
This commit is contained in:
+54
-26
@@ -12,22 +12,39 @@
|
||||
use crate::geometry::Segment;
|
||||
use macroquad::prelude::Vec2;
|
||||
|
||||
// The ordinary rail registrations in `FUN_1000_34ab` use the recovered 0.1
|
||||
// rebound coefficient. The previous 0.82 value made the shooter curve behave
|
||||
// like a rubber bumper and sent the ball back down the launch lane.
|
||||
const WALL_BOUNCE: f32 = 0.10;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TableSegment {
|
||||
pub id: u8,
|
||||
pub segment: Segment,
|
||||
pub normal_rebound: f32,
|
||||
pub tangent_coupling: f32,
|
||||
}
|
||||
|
||||
impl TableSegment {
|
||||
const fn new(id: u8, start: Vec2, end: Vec2) -> Self {
|
||||
// These are the first two Borland Real48 fields in each original
|
||||
// 0x53-byte collision record. They control normal rebound and tangent
|
||||
// coupling in the type-2 response basis.
|
||||
let (normal_rebound, tangent_coupling) = match id {
|
||||
3 | 9 | 25 => (0.10, 0.10),
|
||||
91 | 92 | 94 | 95 | 97 | 98 | 100 | 101 | 103 | 104 => (0.40, 0.0),
|
||||
62 => (0.40, 0.05),
|
||||
15 | 16 | 45 => (0.40, 0.10),
|
||||
78 => (0.50, 0.05),
|
||||
55 | 57 | 59 | 61 | 70 | 72 | 74 | 76 => (0.50, 0.10),
|
||||
66 | 68 | 81 | 83 => (0.50, 0.20),
|
||||
84 | 85 | 86 | 87 | 88 | 90 | 93 | 96 | 99 | 102 | 109 | 110 | 111 | 112 | 113
|
||||
| 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 168 | 170 | 171 | 173 => {
|
||||
(0.60, 0.0)
|
||||
}
|
||||
27 | 28 | 29 | 30 | 31 | 32 | 105 => (0.60, 0.20),
|
||||
_ => (0.60, 0.10),
|
||||
};
|
||||
Self {
|
||||
id,
|
||||
segment: Segment::new(start, end, WALL_BOUNCE),
|
||||
segment: Segment::new(start, end, normal_rebound),
|
||||
normal_rebound,
|
||||
tangent_coupling,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +232,10 @@ pub const BUMPERS: [StaticCircle; 3] = [
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::geometry::{circle_collision, segment_collision};
|
||||
use crate::{
|
||||
geometry::circle_collision,
|
||||
original_physics::{MilliVec, collide_with_line},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn recovered_object_inventory_is_complete() {
|
||||
@@ -241,7 +261,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_recovered_wall_has_a_working_contact_surface_on_both_sides() {
|
||||
fn every_recovered_wall_has_its_registered_one_sided_contact() {
|
||||
for wall in WALLS {
|
||||
let line = wall.segment.end - wall.segment.start;
|
||||
assert!(
|
||||
@@ -251,25 +271,33 @@ mod tests {
|
||||
);
|
||||
let normal = Vec2::new(-line.y, line.x).normalize();
|
||||
let midpoint = (wall.segment.start + wall.segment.end) * 0.5;
|
||||
let mut front_velocity = MilliVec::from_velocity_per_second(normal * 200.0);
|
||||
assert!(
|
||||
collide_with_line(
|
||||
MilliVec::from_position(midpoint - normal),
|
||||
&mut front_velocity,
|
||||
wall.segment.start,
|
||||
wall.segment.end,
|
||||
f64::from(wall.normal_rebound),
|
||||
f64::from(wall.tangent_coupling),
|
||||
),
|
||||
"object {} missed its registered front side",
|
||||
wall.id
|
||||
);
|
||||
|
||||
for side in [-1.0, 1.0] {
|
||||
let outward = normal * side;
|
||||
let mut position = midpoint + outward * 0.5;
|
||||
let mut velocity = -outward * 430.0;
|
||||
let hit = segment_collision(&mut position, &mut velocity, 1.0, wall.segment);
|
||||
|
||||
assert!(hit, "object {} missed its midpoint contact", wall.id);
|
||||
assert!(
|
||||
velocity.dot(outward) >= 0.0,
|
||||
"object {} did not reject an inward trajectory",
|
||||
wall.id
|
||||
);
|
||||
assert!(
|
||||
position.distance(midpoint) >= 0.99,
|
||||
"object {} did not separate the ball from its surface",
|
||||
wall.id
|
||||
);
|
||||
}
|
||||
let mut back_velocity = MilliVec::from_velocity_per_second(-normal * 200.0);
|
||||
assert!(
|
||||
!collide_with_line(
|
||||
MilliVec::from_position(midpoint + normal),
|
||||
&mut back_velocity,
|
||||
wall.segment.start,
|
||||
wall.segment.end,
|
||||
f64::from(wall.normal_rebound),
|
||||
f64::from(wall.tangent_coupling),
|
||||
),
|
||||
"object {} accepted contact from its back side",
|
||||
wall.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user