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:
@@ -16,6 +16,7 @@ pub enum Scenario {
|
||||
Autoplay,
|
||||
Launcher,
|
||||
Flippers,
|
||||
Panel,
|
||||
Claw1,
|
||||
Claw6,
|
||||
Claw7,
|
||||
@@ -28,6 +29,7 @@ impl Scenario {
|
||||
Self::Autoplay => "autoplay",
|
||||
Self::Launcher => "launcher",
|
||||
Self::Flippers => "flippers",
|
||||
Self::Panel => "panel",
|
||||
Self::Claw1 => "claw-1",
|
||||
Self::Claw6 => "claw-6",
|
||||
Self::Claw7 => "claw-7",
|
||||
@@ -41,7 +43,7 @@ impl Scenario {
|
||||
Self::Claw6 => Some(6),
|
||||
Self::Claw7 => Some(7),
|
||||
Self::Claw18 => Some(18),
|
||||
Self::Autoplay | Self::Launcher | Self::Flippers => None,
|
||||
Self::Autoplay | Self::Launcher | Self::Flippers | Self::Panel => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,12 +56,13 @@ impl FromStr for Scenario {
|
||||
"autoplay" => Ok(Self::Autoplay),
|
||||
"launcher" => Ok(Self::Launcher),
|
||||
"flippers" => Ok(Self::Flippers),
|
||||
"panel" => Ok(Self::Panel),
|
||||
"claw-1" => Ok(Self::Claw1),
|
||||
"claw-6" => Ok(Self::Claw6),
|
||||
"claw-7" => Ok(Self::Claw7),
|
||||
"claw-18" => Ok(Self::Claw18),
|
||||
_ => Err(format!(
|
||||
"unknown scenario {value:?}; expected autoplay, launcher, flippers, claw-1, claw-6, claw-7, or claw-18"
|
||||
"unknown scenario {value:?}; expected autoplay, launcher, flippers, panel, claw-1, claw-6, claw-7, or claw-18"
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -143,7 +146,7 @@ fn seconds_to_step(value: &str) -> Result<u64, String> {
|
||||
}
|
||||
|
||||
pub const fn usage() -> &'static str {
|
||||
"Usage:\n tdkpin-rs\n tdkpin-rs --simulate SCENARIO [--at SECONDS | --step N] [--screenshot FILE.png] [--trace FILE.json] [--seed N]\n\nScenarios: autoplay, launcher, flippers, claw-1, claw-6, claw-7, claw-18"
|
||||
"Usage:\n tdkpin-rs\n tdkpin-rs --simulate SCENARIO [--at SECONDS | --step N] [--screenshot FILE.png] [--trace FILE.json] [--seed N]\n\nScenarios: autoplay, launcher, flippers, panel, claw-1, claw-6, claw-7, claw-18"
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
@@ -157,6 +160,7 @@ pub struct Snapshot {
|
||||
pub flippers: FlipperSnapshot,
|
||||
pub launcher_charge: f32,
|
||||
pub collision_id: Option<u8>,
|
||||
pub panel_frame: Option<u16>,
|
||||
pub events: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -194,6 +198,10 @@ pub struct Simulation {
|
||||
impl Simulation {
|
||||
pub fn new(scenario: Scenario, seed: u32) -> Self {
|
||||
let mut game = Game::new_with_seed(1, seed);
|
||||
if scenario == Scenario::Panel {
|
||||
game.panel_frame = Some(0);
|
||||
game.wheel_holes.fill(true);
|
||||
}
|
||||
let initial_events = scenario
|
||||
.claw_terminal()
|
||||
.map_or_else(Vec::new, |terminal| game.begin_claw_scenario(terminal));
|
||||
@@ -274,6 +282,7 @@ impl Simulation {
|
||||
},
|
||||
launcher_charge: self.game.launcher_charge,
|
||||
collision_id: self.game.last_collision_id,
|
||||
panel_frame: self.game.panel_frame,
|
||||
events: events
|
||||
.into_iter()
|
||||
.map(|event| format!("{event:?}"))
|
||||
@@ -317,7 +326,7 @@ fn controls_for(scenario: Scenario, step: u64) -> Controls {
|
||||
..Controls::default()
|
||||
}
|
||||
}
|
||||
Scenario::Claw1 | Scenario::Claw6 | Scenario::Claw7 | Scenario::Claw18 => {
|
||||
Scenario::Panel | Scenario::Claw1 | Scenario::Claw6 | Scenario::Claw7 | Scenario::Claw18 => {
|
||||
Controls::default()
|
||||
}
|
||||
}
|
||||
@@ -432,6 +441,26 @@ mod tests {
|
||||
assert!(!simulation.game.flippers.left_raised);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_scenario_runs_all_frames_and_cleans_up() {
|
||||
let mut simulation = Simulation::new(Scenario::Panel, 1);
|
||||
simulation.advance_to(4);
|
||||
assert_eq!(simulation.game.panel_frame, Some(1));
|
||||
assert_eq!(simulation.trace[4].events, ["Sound(2013)"]);
|
||||
|
||||
simulation.advance_to(1_020);
|
||||
assert_eq!(simulation.game.panel_frame, None);
|
||||
assert_eq!(simulation.game.wheel_holes, [false; 5]);
|
||||
let events = simulation
|
||||
.trace
|
||||
.iter()
|
||||
.flat_map(|snapshot| snapshot.events.iter().map(String::as_str))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(events.iter().filter(|event| **event == "StopSound").count(), 3);
|
||||
assert_eq!(events.iter().filter(|event| **event == "Sound(2013)").count(), 4);
|
||||
assert_eq!(events.iter().filter(|event| **event == "Sound(2012)").count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_claw_scenario_completes_its_seeded_capture() {
|
||||
for scenario in [
|
||||
|
||||
Reference in New Issue
Block a user