fix(audio): suppress tilted drain cue before reset

normal_ball_end asks the sound helper for WAVE 2008 before reset_ball_state
clears Tilt. The helper therefore suppresses the drain cue for a tilted ball.
Rust queued WAVE 2008 unconditionally and only cleared Tilt afterward.

Emit the drain cue only when the pre-reset state is not tilted. Keep later timer
epilogue sounds unchanged, so clearing Tilt can still allow a latched flipper's
normal WAVE 2021 release edge.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 119 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:04:34 +02:00
parent 3859498172
commit d8cb7591b5
3 changed files with 8 additions and 4 deletions
+2 -1
View File
@@ -161,7 +161,8 @@ and this project adheres to
playback and restores bank, trigger, capture, nudge, tilt, and drain cues.
Type-4 WAVE 2004 and bumper WAVE 2006 now precede their score mutations so a
simultaneously crossed media marker leaves WAVE 2007 audible, as in the
monophonic original.
monophonic original. A tilted drain suppresses WAVE 2008 before reset, while
a following flipper-release edge may still play WAVE 2021 after reset.
- Advance media/extra-ball thresholds only from score additions, preserve
32-bit score wrapping, and restore the ninth-diamond ordering: its 24,464
award remains single, then ball-scoped double scoring and all three magnetic
+1 -1
View File
@@ -20,7 +20,7 @@ implementation.
| Subsystem | Rust status | Evidence and boundary |
| --- | --- | --- |
| Artwork | Exact | All 34 custom DIB images, three standard bitmaps, icon, and palette derivatives are preserved in `assets/original/`. The game uses the original 640x460 table, help, ball, wheel, robot, plunger, media, and item frames. DAT400-407 expose the current per-player bonus/collect/release effect at `(55,79)` while preserving the saved 14x14 wheel overlap. Loading presents DAT995 and advances the original DAT994 strip at raw `1000:531e` destination `(202,328)`, height 13, and clipped width `(stage-1)*7` through stages 2-35. |
| Audio | Exact samples and recovered dispatch | All 16 mono PCM WAV resources are embedded unchanged. Playback is emitted at the reconstructed call sites, including multi-sound bank completions and nudge-then-tilt ordering. Like Win16 `SndPlaySound(SND_ASYNC|SND_NODEFAULT)`, each new resource stops the previous one and plays at the host mixer level without an application-side attenuation. WAV 2022 is loaded by the original generic resource loop but has no playback call and is therefore never played by Rust. |
| Audio | Exact samples and recovered dispatch | All 16 mono PCM WAV resources are embedded unchanged. Playback is emitted at the reconstructed call sites, including multi-sound bank completions, nudge-then-tilt ordering, and Tilt suppression of the pre-reset WAVE-2008 drain cue. Like Win16 `SndPlaySound(SND_ASYNC|SND_NODEFAULT)`, each new resource stops the previous one and plays at the host mixer level without an application-side attenuation. WAV 2022 is loaded by the original generic resource loop but has no playback call and is therefore never played by Rust. |
| Help and languages | Exact | Original resource images 1001-1005 are displayed directly. |
| Playfield collision layout | Recovered | All 109 active type-2 line objects and 40 static active type-1 circles are transcribed from the original 175-object registration table. The registration routine converts its sideways inputs with `screen = (y, x - 20)` and accumulates explicitly relative objects. Every static record retains its `+0x49` layer mask; the scanner selects layer 1 below the old Y value 250,000 and layer 2 at or above it, while mask 3 records remain shared. Before detection, the raw `1000:9b69` predicted position must lie in the record bounds derived with the registered five-pixel ball margin. Type-2 records retain every recovered Real48 normal/tangent response pair and registered one-sided orientation. Type-1 records retain their swept-circle radius, radial rebound, tangent coupling, and bumper kick. Detection retains unresolved normal/material candidates so later scan-time motion changes can participate in the selected response. Raw stack slot `SS:...d8a2` makes the first detected record ID win; later nearer candidates are stored but never applied, and both C/Rust have two-candidate regression coverage for this quirk. Each flipper uses its exact two line records plus moving tip circle in both positions. Moving-flipper contact ports `1000:7ed9` rather than fitting live samples: delta-specific pivots/edges, integer cross gates, radial/penetration calculations, response-record gain, and position/velocity publication are tested against all four C harness directions and the raised release geometry. Object 174 is overwritten with the live first ball and Rust handles its ball-to-ball role directly. |
| Ball launcher and nudge input | Recovered | The initial 32-bit fixed-point coordinates decode to `(325, 413)` in the right shooter lane. Each Down keydown subtracts `15*50 = 750` millipixels, release subtracts another `15*100 = 1500`, and the result follows the recovered randomized `-3800` lower and `-2280` weak upper clamp branches. The ten decoration frames use the same strict 750-millipixel thresholds. Left Shift and keypad 3 apply their directional `(50-Random(20))*15` impulses; Space uses the recovered Real48 horizontal factor and `(60-Random(20))*15` vertical impulse. With two balls, the key path still consumes those active-window draws, then applies its separately recovered fixed/shared random slot impulse to both saved velocities. Each nudge adds 25 to the wrapping 16-bit tilt counter, compares it with `30+Random(10)`, and the detail timer decrements a nonzero counter once per callback. Flipper key flags and raised geometry survive ordinary drains; Tilt and a player's final ball clear the flags, lower raised flippers through the normal WAVE-2021 timer edge, and require a physical key release before another press. |
+4 -1
View File
@@ -2088,7 +2088,9 @@ impl Game {
(player.balls == 0 && player.extra_balls == 0).then_some(player.score);
self.save_current_rule_state();
events.push(Event::Drain);
if !self.tilted {
events.push(Event::Sound(2008));
}
if let Some(score) = high_score_candidate {
events.push(Event::HighScoreCandidate(score));
self.flipper_release_latch[0] |= self.flipper_inputs.left_raised;
@@ -4198,10 +4200,11 @@ mod tests {
left_flipper: true,
..Controls::default()
};
game.update(0.030, 3, held);
let events = game.update(0.030, 3, held);
assert!(!game.tilted);
assert!(game.ball.in_launcher);
assert!(!events.contains(&Event::Sound(2008)));
assert!(game.flipper_release_latch[0]);
game.update(0.0, 3, held);
assert!(!game.flipper_inputs.left_raised);