diff --git a/tdkpin-rs/CHANGELOG.md b/tdkpin-rs/CHANGELOG.md index ba2b94a..6224a40 100644 --- a/tdkpin-rs/CHANGELOG.md +++ b/tdkpin-rs/CHANGELOG.md @@ -55,6 +55,9 @@ and this project adheres to chases, magnetic flashes, five-record strip, four-point chase, DAT801-809 item progression, and the circular `WELCOME TO THE MACHINE` BITMAP500 marquee. Move gameplay item frames to their exact `(123,300)` destination. +- Preserve the long-idle signed item-index wrap: phases 19-21 read the adjacent + DAT703/DAT702/DAT701 handles and copy their upper 68x61 regions before later + phases return to the inactive item panel. - Match central score/event side effects: every stored collision candidate clears its ball slot's capture age, Tilt suppresses all score and rule-record mutations, and active multiball doubles points before permanent Double can diff --git a/tdkpin-rs/RECONSTRUCTION.md b/tdkpin-rs/RECONSTRUCTION.md index 3a09adf..a8d6f4e 100644 --- a/tdkpin-rs/RECONSTRUCTION.md +++ b/tdkpin-rs/RECONSTRUCTION.md @@ -29,7 +29,7 @@ the same evidence rather than against this Rust implementation. | Numeric scoring | Recovered gameplay values | Static scores come from the initialized 175-object ledger. Dynamic bumper progression, target-bank completion, diamond awards, 10k-160k lock bonuses, 310k transfer, six effect values, multiball mode, and all four media thresholds are transcribed from `1000:b476`, `1000:c4e1`, `1000:bc36`, and live state probes. The central helper suppresses all mutation during Tilt, applies a 2x active-multiball factor, and then independently applies the active ball's 2x factor, allowing the original 4x stack. Lock and effect awards share the original per-player secondary score and display multiplier; the fifth hole transfers and clears it, increments the multiplier, and grants the recovered ball award. Score mutation uses the original 32-bit wrapping behavior; media markers use the binary's signed-high/unsigned-low comparison, and each add operation can advance at most one threshold. Type-4 WAVE 2004, bumper WAVE 2006, and bank-completion WAVE 2017 dispatch before their associated score mutations, leaving a crossed marker's WAVE 2007 as the audible asynchronous sample. Rendering separately follows `1008:0996`'s eight-place 130x40 fields/right edge X=606 and `1008:0b9e`'s strict signed status thresholds; configured-player rows and 5x5 remaining-ball markers retain their exact dimensions and current/noncurrent counts. | | High scores | Recovered visible flow; portable storage | The original 276-byte table is decoded as ten `IWIK`-XOR-obfuscated little-endian scores plus ten 22-byte names. Each player is checked immediately when their own last ball is lost; qualifying scores use the original signed-high/unsigned-low comparison and a `TDK Pinball Player`-prefilled DIALOG 32514 flow with the exact caption, prompt, 21-character limit, OK/Cancel semantics, and allowance for an empty accepted name. The `HighScore` child uses exact DAT993 at 360x300, heading `(180,27)`, name/rank X=30, right-aligned score X=320, and ten 20-pixel rows before play resumes. Persisted updates use portable JSON rather than rewriting the Win16 file. | | Configuration | Recovered behavior; portable follow-up storage | Before portable JSON exists, Rust reads the original case-insensitive `[Settings]` keys `Language`, `Speed`, and singular `Sound` from the executable-adjacent INI, falling back to the embedded distributed TDKPIN.INI. Values retain the Win16 defaults/clamps; the shipped file therefore selects German, speed 3, and sound-on because its plural `Sounds` line does not override singular `Sound`. F12 toggles only runtime playback without UI or persistence. Later explicit F10 changes use the platform user-data directory. | -| Windows UI shell | Original gameplay surface with native host shell | Win16 window ownership and GDI calls are replaced by a fixed native window, but the visible game, help resources, automatic per-player high-score flow, original keys, and 640x460 logical pixels are retained. Idle mode advances on the selected detail callback and reproduces `idle_transition_tick`: target/word-quad/record-strip chases, magnetic flashes, four-point chase, DAT801-809 item progression, and the circular BITMAP500 `WELCOME TO THE MACHINE` marquee composited over exact DAT997 regions. Add-player, nudge, and F12 actions dispatch on Key-Up through the recovered release handler. Both key handlers honor the four-child auxiliary-window gate, so F1/F12 cannot control the main game through Help, HighScore, or name-entry UI. The low-byte scan aliases are preserved: both Ctrl keys map left, both Enter keys map right, and keypad plus or the physical main-keyboard `0x1b` plus/right-bracket position adds players. F2/F3 remain unassigned as in normal original operation; optional portable settings and table viewers use F10/F9 and do not replace gameplay input. | +| Windows UI shell | Original gameplay surface with native host shell | Win16 window ownership and GDI calls are replaced by a fixed native window, but the visible game, help resources, automatic per-player high-score flow, original keys, and 640x460 logical pixels are retained. Idle mode advances on the selected detail callback and reproduces `idle_transition_tick`: target/word-quad/record-strip chases, magnetic flashes, four-point chase, DAT801-809 item progression, its signed long-idle wrap through the adjacent DAT703/DAT702/DAT701 handles, and the circular BITMAP500 `WELCOME TO THE MACHINE` marquee composited over exact DAT997 regions. Add-player, nudge, and F12 actions dispatch on Key-Up through the recovered release handler. Both key handlers honor the four-child auxiliary-window gate, so F1/F12 cannot control the main game through Help, HighScore, or name-entry UI. The low-byte scan aliases are preserved: both Ctrl keys map left, both Enter keys map right, and keypad plus or the physical main-keyboard `0x1b` plus/right-bracket position adds players. F2/F3 remain unassigned as in normal original operation; optional portable settings and table viewers use F10/F9 and do not replace gameplay input. | ## Extracted asset inventory diff --git a/tdkpin-rs/src/app.rs b/tdkpin-rs/src/app.rs index 436ee74..a99fc01 100644 --- a/tdkpin-rs/src/app.rs +++ b/tdkpin-rs/src/app.rs @@ -159,6 +159,15 @@ impl AttractAnimation { (item != 0).then(|| usize::try_from(item - 1).unwrap_or(0)) } + fn wrapped_status_item_index(self) -> Option { + match self.tick / 1_152 { + 19 => Some(2), + 20 => Some(1), + 21 => Some(0), + _ => None, + } + } + fn marquee_source_x(self) -> Option { (self.score_counter != 0).then(|| { let phase = self.score_counter % 49; @@ -611,6 +620,16 @@ impl App { 300.0, WHITE, ); + } else if let Some(status) = self.attract.wrapped_status_item_index() { + draw_texture_region( + &self.assets.media[status], + 123.0, + 300.0, + 68.0, + 61.0, + 0.0, + 0.0, + ); } self.draw_intro_marquee(); } @@ -1405,6 +1424,10 @@ mod tests { assert_eq!(at(10_368).item_index(), Some(8)); assert_eq!(at(11_520).item_index(), Some(7)); assert_eq!(at(20_736).item_index(), None); + assert_eq!(at(21_888).wrapped_status_item_index(), Some(2)); + assert_eq!(at(23_040).wrapped_status_item_index(), Some(1)); + assert_eq!(at(24_192).wrapped_status_item_index(), Some(0)); + assert_eq!(at(25_344).wrapped_status_item_index(), None); } #[test] diff --git a/tdkpin-rs/src/simulation.rs b/tdkpin-rs/src/simulation.rs index 0def3db..23498ef 100644 --- a/tdkpin-rs/src/simulation.rs +++ b/tdkpin-rs/src/simulation.rs @@ -9,7 +9,7 @@ use std::{ pub const SIMULATION_HZ: u32 = 120; const SIMULATION_HZ_F64: f64 = 120.0; const SIMULATION_DT: f32 = 1.0 / 120.0; -const MAX_SIMULATION_STEPS: u64 = 72_000; +const MAX_SIMULATION_STEPS: u64 = 100_000; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Scenario { @@ -287,7 +287,7 @@ impl Simulation { } fn record(&mut self, events: Vec) { - let step = u32::try_from(self.step).expect("simulation step is limited to 72000"); + let step = u32::try_from(self.step).expect("simulation step is limited to 100000"); let claw_bank = match self.game.claw.bank { ClawSpriteBank::Closing => "closing", ClawSpriteBank::Opening => "opening",