fix(game): restore the 281-frame panel sequence
Replace the immediate five-hole cleanup with the original 281-callback panel state machine. Suspend physics, clear contact/item state at the recovered boundaries, and emit WAVE 2013, WAVE 2012, and stop events at frames 1/45/58/66/129/159/222/230/238 before resuming after cleanup. Decode DAT600's four AND/OR mask-image pairs into transparent native textures and render all nine phase formulas with the original source rectangles, destination offsets, variable heights, and opaque wheel copies. Add a `panel` simulation scenario for deterministic trace and framebuffer validation. Test Plan: - `cargo test --all-targets` -- passed, 72 tests - `cargo clippy --all-targets -- -D warnings` -- passed - `rumdl check CHANGELOG.md README.md RECONSTRUCTION.md` -- passed - rendered panel frames 1, 66, 159, 238, and completion -- visually verified - `git diff --cached --check` -- passed
This commit is contained in:
+67
-2
@@ -66,6 +66,7 @@ pub enum Event {
|
||||
Tilt,
|
||||
Drain,
|
||||
HighScoreCandidate(u32),
|
||||
StopSound,
|
||||
Sound(u16),
|
||||
}
|
||||
|
||||
@@ -87,7 +88,8 @@ impl Event {
|
||||
| Self::Nudge
|
||||
| Self::Tilt
|
||||
| Self::Drain
|
||||
| Self::HighScoreCandidate(_) => None,
|
||||
| Self::HighScoreCandidate(_)
|
||||
| Self::StopSound => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,6 +269,7 @@ pub struct Game {
|
||||
pub claw: Claw,
|
||||
pub bumper_flash: [f32; 3],
|
||||
pub wheel_animation: f32,
|
||||
pub panel_frame: Option<u16>,
|
||||
pub nudge_shake: f32,
|
||||
pub launcher_charge: f32,
|
||||
pub finished: bool,
|
||||
@@ -310,6 +313,7 @@ impl Game {
|
||||
claw: Claw::default(),
|
||||
bumper_flash: [0.0; 3],
|
||||
wheel_animation: 0.0,
|
||||
panel_frame: None,
|
||||
nudge_shake: 0.0,
|
||||
launcher_charge: 0.0,
|
||||
finished: false,
|
||||
@@ -452,7 +456,8 @@ impl Game {
|
||||
|
||||
fn timer_tick(&mut self, elapsed: f32, substeps: u8, events: &mut Vec<Event>) {
|
||||
self.update_claw(elapsed, events);
|
||||
if !self.claw.ball_suspended {
|
||||
let panel_active = self.update_panel_completion(events);
|
||||
if !self.claw.ball_suspended && !panel_active {
|
||||
for _ in 0..substeps {
|
||||
self.fixed_update(STEP_SECONDS, events);
|
||||
self.advance_secondary_ball(events);
|
||||
@@ -488,6 +493,31 @@ impl Game {
|
||||
self.apply_flipper_kicks();
|
||||
}
|
||||
|
||||
fn update_panel_completion(&mut self, events: &mut Vec<Event>) -> bool {
|
||||
let Some(frame) = self.panel_frame else {
|
||||
return false;
|
||||
};
|
||||
if frame >= 281 {
|
||||
self.panel_frame = None;
|
||||
self.wheel_holes.fill(false);
|
||||
return true;
|
||||
}
|
||||
if frame == 1 {
|
||||
for record_id in 129..=133 {
|
||||
self.record_contacts[record_id] = 0;
|
||||
}
|
||||
}
|
||||
let next = frame + 1;
|
||||
self.panel_frame = Some(next);
|
||||
match next {
|
||||
1 | 66 | 159 | 238 => events.push(Event::Sound(2013)),
|
||||
45 | 129 | 222 => events.push(Event::StopSound),
|
||||
58 | 230 => events.push(Event::Sound(2012)),
|
||||
_ => {}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn advance_tilt_counter(&mut self, elapsed: f32) {
|
||||
self.tilt_counter_accumulator += elapsed;
|
||||
while self.tilt_counter_accumulator >= self.claw_frame_seconds {
|
||||
@@ -1025,6 +1055,7 @@ impl Game {
|
||||
player.secondary_score = 0;
|
||||
player.score_multiplier = player.score_multiplier.wrapping_add(1);
|
||||
player.extra_balls = player.extra_balls.wrapping_add(1);
|
||||
self.panel_frame = Some(0);
|
||||
}
|
||||
self.reset_ball_to_launcher();
|
||||
events.push(Event::Lock);
|
||||
@@ -1354,6 +1385,7 @@ impl Game {
|
||||
self.tilt_counter_accumulator = 0.0;
|
||||
self.bumper_flash.fill(0.0);
|
||||
self.wheel_animation = 0.0;
|
||||
self.panel_frame = None;
|
||||
self.claw = Claw::default();
|
||||
self.flipper_inputs = Flippers::default();
|
||||
self.flippers = Flippers::default();
|
||||
@@ -2410,6 +2442,39 @@ mod tests {
|
||||
assert_eq!(game.capture_age, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_completion_uses_all_281_frames_and_original_sound_boundaries() {
|
||||
let mut game = Game::new(1);
|
||||
game.panel_frame = Some(0);
|
||||
game.wheel_holes.fill(true);
|
||||
for record_id in 129..=133 {
|
||||
game.record_contacts[record_id] = 99;
|
||||
}
|
||||
let mut events = Vec::new();
|
||||
|
||||
for _ in 0..282 {
|
||||
game.timer_tick(0.030, 0, &mut events);
|
||||
}
|
||||
|
||||
assert_eq!(game.panel_frame, None);
|
||||
assert_eq!(game.wheel_holes, [false; 5]);
|
||||
assert!(game.record_contacts[129..=133].iter().all(|contact| *contact == 0));
|
||||
assert_eq!(
|
||||
events,
|
||||
[
|
||||
Event::Sound(2013),
|
||||
Event::StopSound,
|
||||
Event::Sound(2012),
|
||||
Event::Sound(2013),
|
||||
Event::StopSound,
|
||||
Event::Sound(2013),
|
||||
Event::StopSound,
|
||||
Event::Sound(2012),
|
||||
Event::Sound(2013),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claw_release_table_decodes_the_original_thousandth_pixel_coordinates() {
|
||||
for (frame, expected_position, expected_velocity) in [
|
||||
|
||||
Reference in New Issue
Block a user