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
This commit is contained in:
2026-08-23 20:38:31 +02:00
parent 26b7a9ecf3
commit 1dff82aef6
3 changed files with 11 additions and 9 deletions
+2
View File
@@ -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.
+1 -1
View File
@@ -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
+8 -8
View File
@@ -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