From 1dff82aef658f98c572c7973ee33b2f7ba8fa686 Mon Sep 17 00:00:00 2001 From: ddidderr Date: Sun, 23 Aug 2026 20:38:31 +0200 Subject: [PATCH] fix(input): dispatch release-handler actions on key-up The original key-release handler owns add-player, all three nudge actions, and F12 sound toggling. Rust dispatched those actions on the initial key press, which changed launcher/player timing and applied nudges before the player released the key. Use Macroquad's key-up edges for every recovered release-handler action. Keep F1 on key-down, flippers as held key bytes, and the launcher as press/repeat plus release, matching their separate binary paths. Test Plan: - `cargo test --workspace --all-targets --all-features` -- 125 passed - `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed - `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed - `git diff --cached --check` -- passed --- tdkpin-rs/CHANGELOG.md | 2 ++ tdkpin-rs/RECONSTRUCTION.md | 2 +- tdkpin-rs/src/app.rs | 16 ++++++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/tdkpin-rs/CHANGELOG.md b/tdkpin-rs/CHANGELOG.md index 2ea2003..81b8f46 100644 --- a/tdkpin-rs/CHANGELOG.md +++ b/tdkpin-rs/CHANGELOG.md @@ -178,6 +178,8 @@ and this project adheres to - Match the original low-byte keyboard scans: both Ctrl keys operate the left flipper, both Enter keys operate the right, and the physical `0x1b` main-keyboard plus/right-bracket position starts or adds players. +- Dispatch add-player, nudge, and F12 sound-toggle actions on key release through + the original `1000:638e` timing rather than on their initial key press. - Route a single-ball type-3 completion during Tilt through `1000:ae6e` instead of granting a free launcher reset: lock holes consume the ball, while record 148 can still take its required-state special-respawn branch. diff --git a/tdkpin-rs/RECONSTRUCTION.md b/tdkpin-rs/RECONSTRUCTION.md index 8b0dfb7..39dfb94 100644 --- a/tdkpin-rs/RECONSTRUCTION.md +++ b/tdkpin-rs/RECONSTRUCTION.md @@ -29,7 +29,7 @@ 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. 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, 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. 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 c281864..14cb738 100644 --- a/tdkpin-rs/src/app.rs +++ b/tdkpin-rs/src/app.rs @@ -46,8 +46,8 @@ fn bumper_value_indicator_count(value: u32) -> usize { .min(BUMPER_VALUE_REGIONS.len()) } -fn add_player_pressed() -> bool { - ADD_PLAYER_KEYS.into_iter().any(is_key_pressed) +fn add_player_released() -> bool { + ADD_PLAYER_KEYS.into_iter().any(is_key_released) } fn visible_score_digits(mut value: u32) -> Vec<(u8, u8)> { @@ -285,7 +285,7 @@ impl App { } fn handle_global_input(&mut self) { - if is_key_pressed(KeyCode::F12) { + if is_key_released(KeyCode::F12) { self.sounds_enabled = !self.sounds_enabled; } if is_key_pressed(KeyCode::F1) && self.screen != Screen::NameEntry { @@ -325,7 +325,7 @@ impl App { fn update_attract(&mut self) { self.attract .update(get_frame_time(), self.saved.settings.speed); - if add_player_pressed() { + if add_player_released() { self.game = Some(Game::new(1)); self.screen = Screen::Playing; self.assets.play(2001, self.sounds_enabled); @@ -333,7 +333,7 @@ impl App { } fn update_game(&mut self) { - if add_player_pressed() + if add_player_released() && self .game .as_mut() @@ -349,11 +349,11 @@ impl App { || is_key_down(KeyCode::Enter) || is_key_down(KeyCode::D) || is_key_down(KeyCode::Right); - let nudge = if is_key_pressed(KeyCode::LeftShift) { + let nudge = if is_key_released(KeyCode::LeftShift) { Nudge::Left - } else if is_key_pressed(KeyCode::RightShift) || is_key_pressed(KeyCode::Kp3) { + } else if is_key_released(KeyCode::RightShift) || is_key_released(KeyCode::Kp3) { Nudge::Right - } else if is_key_pressed(KeyCode::Space) { + } else if is_key_released(KeyCode::Space) { Nudge::Center } else { Nudge::None