Commit Graph
100 Commits
Author SHA1 Message Date
ddidderr dd0299c30b fix(attract): use remainder-driven item phases
Raw 1000:061c-063b divides the idle tick by 144, copies the remainder from
CX:BX into AX:DX, and only then divides by 8. Readable C and Rust instead used
an unbounded quotient equivalent to tick/1152, making DAT801-809 advance far
too slowly and suggesting a nonexistent negative long-idle index.

Use `(tick % 144) / 8` to repeat item states 0,1..9,8..1 every 144 callbacks.
Remove the disproven long-idle status crops and restore the ten-minute simulator
ceiling. Extend the C and Rust phase tests across the forward, reverse, and wrap
boundaries.

Test Plan:
- raw instruction review at `1000:061c-063b` -- remainder transfer confirmed
- `cargo test --workspace --all-targets --all-features` -- 125 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed with zero incomplete or unclassified units
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 20:50:05 +02:00
ddidderr 5b8351ca1f fix(ui): reproduce long-idle item index wrap
The attract item animation computes `18 - phase` as a signed value without a
lower bound. At phases 19 through 21, the subsequent 16-bit handle index wraps
backward from the DAT801 table into DAT703, DAT702, and DAT701, copying each
bitmap's upper 68x61 region into the item panel. Rust stopped drawing after
phase 18 and hid this observable long-idle behavior.

Render those three adjacent status crops and return to the inactive panel for
later phases. Raise the deterministic simulation ceiling to 100,000 frames so
the first wrapped phase at 10.94 minutes can be captured and inspected.

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
- `cargo run -- --simulate attract --step 78797 --screenshot /tmp/tdkpin-attract-wrap-19.png` -- passed; DAT703 crop visually inspected at 640x460
- `git diff --cached --check` -- passed
2026-08-23 20:45:54 +02:00
ddidderr db0ec0bdc4 fix(random): continue Borland seed across games
The original RandSeed is program-global and is initialized once at startup.
Rust stored the exact Borland generator inside Game but discarded its final
state at game over and seeded every following game from Macroquad again,
breaking stream continuity across high-score/attract flow.

Expose the current seed, retain it when the App consumes a finished game, and
construct the next game from that value. Attract, high-score, and portable UI
continue to consume no gameplay draws.

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
2026-08-23 20:43:10 +02:00
ddidderr 2a17fbeeef docs(audit): align validation claims with current evidence
The reconstruction ledger still described an obsolete function count, claimed
local Windows/macOS cross-target checks despite those standard libraries being
absent, and attributed lock, claw, drain, and repeated-launch coverage to the
current autoplay trace. Those statements were stronger than the current
artifacts prove after the corrected trajectory.

Record the final C ledger counts, distinguish configured native CI from local
Linux production validation, add both stopped-Wine slingshot probes, and route
claw/panel/lock/drain evidence to the deterministic and focused tests that
actually cover it.

Test Plan:
- `rumdl check --flavor commonmark RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 20:41:06 +02:00
ddidderr 0c4c18dbea fix(input): honor auxiliary-window key gate
Both original key handlers return without processing gameplay keys while any of
the four child-window slots is occupied. Rust continued to toggle sound through
Help, HighScore, and name-entry UI and used F1 to close Help, behavior the main
Win16 window cannot perform through an active child.

Limit recovered F1 and F12 handling to the attract and playing screens. The
portable Enter/Escape Help return remains available as an explicit modern
control without bypassing the original main-window gate.

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
2026-08-23 20:39:08 +02:00
ddidderr 1dff82aef6 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
2026-08-23 20:38:31 +02:00
ddidderr 26b7a9ecf3 fix(input): match original low-byte key scans
The Win16 handlers mask the keyboard scan to its low byte. Main and keypad
Enter therefore both produce the right-flipper scan 0x1c, both Ctrl keys
produce the left-flipper scan 0x1d, and scan 0x1b is the physical
main-keyboard plus/right-bracket position accepted alongside keypad plus. Rust
omitted main Enter and scan 0x1b, and incorrectly assigned Right Ctrl to the
right flipper.

Map the native keys to those recovered scan semantics. Keep A/D, arrows, and
Equal only as explicit modern aliases and document the distinction.

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 README.md RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 20:37:45 +02:00
ddidderr 505bf0417a fix(rules): finish tilted type-three captures
The original capture handler does not request a launcher reset when a single
ball completes a type-three record during Tilt. It calls the normal ball-end
state machine instead. Rust returned Reset for every single-ball capture,
granting tilted lock-hole captures a free ball and bypassing record 148's
special-respawn decision.

Add an explicit Finish action for tilted single-ball type-three completions.
Lock holes now consume the ball with the tilted drain cue suppressed; record
148 still publishes its required contact first, so the same finish call can
consume it through the special-respawn branch without using a marker.

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
2026-08-23 20:36:29 +02:00
ddidderr c1790c7ff6 fix(multiball): start completed panel on type-three reset
The original reset helper checks contact words 129 through 133 and starts the
DAT600 panel whenever all five are nonzero. This matters after the fifth lock is
captured during multiball: the panel is initially deferred, but a later
single-ball type-three reset, including record 148, starts it even without a
second lock-hole award. Rust only started the panel from its lock completion
helper.

Mirror the five-contact gate in the shared type-three launcher reset. Extend the
multiball completion regression to prove both the special-hole reset path and
the separately capped survivor recapture path.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 124 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
2026-08-23 20:34:34 +02:00
ddidderr 23195cb9d1 fix(physics): restore standard tangent sign response
The left relative slingshot probe proved the spin projection, but its geometry
did not distinguish the sign rule. A stopped-Wine probe on symmetric record 72
did: the binary uses the standard dot-product tangent for both spin and the
opposing direction of the current tangential response. The earlier
swapped-component interpretation happened to agree for vertical walls and a
centered circle but selected the wrong sign on the right slingshot.

Use `tv=(vx*nx+vy*ny)/length`, update spin with
`tv*0.02*response_tangent`, and apply
`-sign(tv)*abs(normal_velocity)*response_tangent`, retaining the binary's
negative direction when tv is zero. Seal record 72's exact position, velocity,
and `+5.46` Real48 spin in C and Rust. Keep the two-minute autoplay assertion
focused on actual launch, press/release, target, bumper, and finite-state
activity after the corrected trajectory.

Test Plan:
- stopped Wine 11.15 record-72 injection/trace -- matched position, velocity, and spin
- `cargo test --workspace --all-targets --all-features` -- 124 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed with zero incomplete or unclassified units
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 20:33:20 +02:00
ddidderr f79652bc9f fix(physics): restore relative slingshot response
A full comparison against the 175-record ledger found stale guessed coordinates
in both relative slingshot chains. Circles 56/58/71/73 and lines 57/59/72/74
were displaced by up to 70 pixels. The corrected record-57 geometry then
exposed a second issue: readable C and Rust had collapsed two independent
binary response projections into one.

Transcribe the accumulated initializer coordinates exactly. Use the
swapped-component projection only to sign the current tangential response
`abs(normal_velocity) * response_tangent`, and use the standard dot product for
`spin_delta * 0.02 * response_tangent`. Correct the readable C evidence and
Rust together, extend live tracing with the six-byte spin field, and seal the
stopped-Wine transition from `(75530,354765)/(3000,15)` to
`(77369,356597)/(1839,1832)` with Real48 spin `-5.28` in both harnesses.

Test Plan:
- stopped Wine 11.15 record-57 injection/trace -- matched position, velocity, and spin
- `cargo test --workspace --all-targets --all-features` -- 123 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed with zero incomplete or unclassified units
- `python3 -m py_compile original/tools/trace_original_state.py original/tools/inject_original_state.py` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 20:28:29 +02:00
ddidderr 9b56247c32 fix(audio): order bank completion cues before awards
The original bank handlers play WAVE 2017 before adding either the 50,000
maximum bumper-bank bonus or a 10,000 through 24,464 TDK award. Rust added the
award first. When that operation crossed a media marker, its WAVE 2007 cue was
therefore replaced by the late completion sound.

Queue both completion cues before their score operations. Exact regressions put
each bank one award below the first threshold and prove the monophonic resource
sequence 2012, 2017, 2007.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 121 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
2026-08-23 20:13:33 +02:00
ddidderr f53cd5ab2b fix(ui): render partial target-bank states
Records 90 through 104 and 109 through 120 deactivate in linked three-line
groups. For each group, the flag-bearing head switches its recovered bounds
from overlay B to overlay A until the complete bank rearms every record. Rust
updated collision activity but never drew these nine intermediate states.

Render the five bumper-bank and four TDK-bank head regions directly from the
175-record ledger. Extend the bank regression to prove partial activation and
post-completion clearing, and seed two partial groups in the deterministic
rules framebuffer.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 120 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `cargo run -- --simulate targets --step 26 --screenshot /tmp/tdkpin-targets-banks.png` -- passed; partial bumper and TDK bank regions visually inspected at 640x460
- `git diff --cached --check` -- passed
2026-08-23 20:12:45 +02:00
ddidderr 71938c0062 fix(ui): render cumulative bumper-value items
The five word quads at indices 1 through 5 are the visible progression for the
recovered bumper-value byte. The original activates them cumulatively as the
value rises from 1,000 to 6,000. Rust implemented the score progression but did
not render any of these player items during gameplay.

Share the exact quad bounds with the attract animation and draw the first
value/1000-1 regions from DAT997. Seed the deterministic targets scenario with
the maximum recovered value so framebuffer validation covers all five lamps.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 120 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `cargo run -- --simulate targets --step 26 --screenshot /tmp/tdkpin-targets-bumper.png` -- passed; all five recovered regions visually inspected at 640x460
- `git diff --cached --check` -- passed
2026-08-23 20:11:06 +02:00
ddidderr ac254f9e45 fix(ui): render armed record 148 item region
Record 148 publishes player item 20 and refreshes word-quad 20 through overlay
A. Its recovered bounds are (9,14)-(25,30). Rust tracked the armed special hole
but never rendered this visible 17x17 table state.

Expose the armed/active special-hole state and composite the exact DAT997 region
until the special respawn consumes record 148's contact. Make the deterministic
effect-seven scenario internally valid by seeding the required contact, so its
framebuffer now covers both the DAT407 effect and item 20.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 120 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `cargo run -- --simulate effect-7 --step 0 --screenshot /tmp/tdkpin-effect7-special.png` -- passed; visually inspected at 640x460
- launcher/effect screenshot comparison -- 136 RGB pixels differ inside only the expected 17x17 item region
- `git diff --cached --check` -- passed
2026-08-23 20:09:55 +02:00
ddidderr 8d436b8a4b fix(rules): preserve lock holes when arming effect seven
Record 148 writes player item 20 and its own type-three contact sentinel. It
does not clear lock-hole items or contacts 129 through 133; the panel completion
path owns that reset. Rust treated record 148 as a wheel-reset sensor and erased
all five lock holes immediately.

Rename the recovered mechanism to the special/effect-seven hole and preserve
existing lock progress when it is captured. Keep its multiball-ready state,
contact 2 publication, and later special-respawn suppression unchanged.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 120 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
2026-08-23 20:07:39 +02:00
ddidderr 2d14823c04 fix(multiball): defer fifth-lock panel for survivor
The fifth type-three lock always pays and transfers its accumulated award, but
the original starts the DAT600 panel only when no multiball is active. Rust
started the panel unconditionally and suspended the surviving ball. A later
capture with all five contacts already set could also shift the award to
320,000 instead of retaining the binary's 160,000 cap.

Derive each lock award from the post-capture filled count capped at five. Keep
the transfer, multiplier increment, and extra ball during multiball, but defer
the panel until a single-ball completion. Cover the fifth multiball capture and
its survivor recapture end to end.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 120 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
2026-08-23 20:06:27 +02:00
ddidderr d8cb7591b5 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
2026-08-23 20:04:34 +02:00
ddidderr 3859498172 fix(input): preserve Win16 flipper key latches
Normal ball-end handling keeps the original flipper key bytes and geometry
unless the current player has finished. Rust instead reset both flippers on
every drain, skipped the normal WAVE 2021 release edge, and treated a key still
held after Tilt as a fresh press once Tilt cleared.

Preserve flipper state across ordinary drains. Tilt and a player's final ball
now clear only the key flags, let the timer epilogue lower raised geometry, and
latch each physical key until key-up before accepting another press. This
matches the original WM_KEYDOWN/WM_KEYUP lifetime while retaining modern
per-frame input sampling.

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
2026-08-23 20:04:07 +02:00
ddidderr 25019514c7 fix(multiball): clear record 148 contact on respawn
The required-effect word at 1028:399a is record 148's +0x43 contact field.
The original special respawn clears that exact word before suppressing the
capture gate. Rust reset only its derived multiball state and left contact 2
behind, allowing a synthetic type-three rim candidate after respawn.

Clear record_contacts[148] with the derived state. Extend both armed and active
multiball respawn regressions to begin with the binary's contact sentinel and
prove that the transition removes it.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 118 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `rumdl check --flavor commonmark CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 20:02:32 +02:00
ddidderr a7c2779f73 fix(rules): retain active target rotation across drains
The original timer advances DAT600 target rotation only from active_ball_tick.
A normal ball end does not clear its phase, so a pending rotation pauses while
the next ball waits in the launcher and resumes after launch. Rust advanced the
phase during launcher idle and then discarded it on drain.

Gate rotation updates with the active-ball state and stop clearing rotation,
panel, or claw mechanism state in the normal drain path where the binary leaves
them untouched. Seed the deterministic target scenario with an active ball so
it continues to exercise all six recovered phases.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 118 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
2026-08-23 20:01:00 +02:00
ddidderr 6e6b7f4c58 fix(rules): scope double scoring to the active ball
The binary stores the diamond-bank double flag at receiver +0x0bdc and clears
it in normal_ball_end. Rust stored that flag on Player, so it incorrectly
survived every normal drain and player turn. The special effect-seven respawn
is intentionally different because it bypasses normal_ball_end.

Represent the flag as an explicit ball-double state on Game. Clear it only on
the normal ball-end path, preserve it through the special respawn, and make the
first completed diamond bank after a normal drain re-enable double scoring
without adding 100,000 secondary points. Keep the independent multiball factor,
so both active factors still stack to 4x.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 117 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
2026-08-23 19:58:39 +02:00
ddidderr df66530b86 fix(scoring): preserve bumper sound precedence
Bumper flags are dispatched before the variable bumper award in the original
collision response. Rust duplicated the primary- and secondary-ball rule path
and appended WAVE 2006 after score mutation, which hid WAVE 2007 whenever that
award crossed a media/extra-ball threshold.

Share one bumper-rule helper for both ball slots, arm the five-callback visual
countdown, dispatch WAVE 2006, and only then add the weak or kicked bumper score.
An exact event-order regression keeps the threshold sound as the final audible
sample.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 116 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
2026-08-23 19:56:41 +02:00
ddidderr 50764f6151 fix(scoring): restore marker comparison and hit sound order
The original score helper compares signed high words and unsigned low words,
which is a signed 32-bit comparison of the wrapped score bits. Rust used an
unsigned comparison and could award a media marker after the score crossed the
sign bit. Type-four trigger handling also queued WAVE 2004 after score mutation,
so it replaced WAVE 2007 when the same hit crossed an extra-ball threshold.

Use the binary's comparison semantics and dispatch the type-four hit sound
before both ordinary target and player-effect score mutations. Regression tests
cover both trigger paths, the exact monophonic event order, and a score with the
high bit set.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 115 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
2026-08-23 19:55:44 +02:00
ddidderr 4a55150fc0 fix(rules): restore ninth-diamond completion order
The reconstructed collision response dispatches record flags before adding the
selected record's static score. Rust previously scored first and delayed the
permanent-double transition until a later completed bank, leaving the three
magnetic records inactive and producing the wrong ninth-bank total.

Apply flag-driven rules first, keep the 24,464 completion award at the old
single multiplier, then activate permanent double scoring and records 153, 6,
and 154 before the final 1,500-point collision score. Subsequent completed
banks now immediately add 100,000 to the per-player secondary score. Update the
regression test and reconstruction notes with the observable order.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- 114 passed
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `rumdl check --flavor commonmark RECONSTRUCTION.md CHANGELOG.md` -- passed
- `git diff --cached --check` -- passed
- `cargo +nightly fmt -- --check` -- reports pre-existing whole-project diffs
2026-08-23 19:52:28 +02:00
ddidderr 6f8c55657b fix(physics): restore swapped-component tangent driver
Use the binary tangent velocity -(vx*normal_y + vy*normal_x)/length, then combine it with retained spin and the Real48 response coefficient. This single formula matches live outer/inner shooter walls, object 155, bumper 51, and the horizontal special-drain response; update C evidence and keep autoplay coverage on a target-reaching seed.

Test Plan:
- bash original/tools/test_reconstructed_c.sh
- python3 original/tools/audit_reconstruction.py --require-complete
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- original Wine traces: /tmp/original-outer-wall.csv /tmp/original-inner-wall.csv /tmp/original-circle155-after.csv /tmp/original-special-respawn.csv
- git diff --check
2026-08-23 19:48:30 +02:00
ddidderr 3dce159965 fix(physics): match live tangent response transitions
Correct the common projection to spin*0.4 + normal_velocity*response_tangent and seal line/circle behavior against current Wine one-substep traces. Preserve record-two's in-response special-respawn continuation, producing the live (16698,21191)/(-302,-1809) state after publishing (17000,23000)/(0,3040), and update the readable C evidence.

Test Plan:
- bash original/tools/test_reconstructed_c.sh
- python3 original/tools/audit_reconstruction.py --require-complete
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md
- live Wine record 2 trace: /tmp/original-special-respawn.csv
- live Wine object 155 trace: /tmp/original-circle155-after.csv
- git diff --check
2026-08-23 19:42:23 +02:00
ddidderr 06d1010431 fix(physics): gate dynamic impulse on capture age
Match records 174/175 by transferring normal impulse only when the other slot capture age is nonpositive. A held slot now keeps its velocity and the moving ball resolves with the full normal velocity; successful transfer retains the original normal_velocity-1000 response.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 19:32:23 +02:00
ddidderr 6c879a3f91 fix(game): restore effect-seven special respawn
Track required effect-seven state through active multiball and implement 1000:ae6e's final single-ball respawn at record 148 without consuming a player marker. Preserve velocity, clear required state, and hold the 0875-style record-148 gate until a broadphase-active type-four record reenables it.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 19:30:38 +02:00
ddidderr d80f823d4b fix(physics): restore four-sided board bounds
Finish a ball before record scanning when its clamped prediction leaves x=(0,340000] or y=(0,460000], and remove only the escaping slot during multiball. Delete the invented post-move y>470 shortcut so collision responses that land outside persist until the original next-substep check.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 19:25:17 +02:00
ddidderr f4e6a5a372 fix(av): restore original nudge and sound presentation
Remove the invented whole-frame nudge shake so nudges affect only recovered ball state, sound, and tilt behavior. Match monophonic SndPlaySound semantics by stopping the current WAV before each new resource and using full host mixer volume instead of an arbitrary 72 percent attenuation.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 19:23:24 +02:00
ddidderr c84df73857 fix(ui): restore effect and player display assets
Load and render DAT400-407 effect panels with the preserved wheel overlap, and add deterministic effect-7 coverage. Restore exact 1008:0996/0b9e score fields, configured-player rows, current/status indicators, signed status thresholds, and six 5x5 per-player ball-marker slots.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- cargo run --quiet -- --simulate effect-7 --step 0 --screenshot /tmp/tdkpin-effect7.png
- cargo run --quiet -- --simulate launcher --step 0 --screenshot /tmp/tdkpin-display.png
- git diff --check
2026-08-23 19:21:28 +02:00
ddidderr 0643138c0e fix(ui): restore exact score and status displays
Render 1008:0996's eight-place 130x40 score fields at x=478 through right edge 606, exact configured-player rows, current/status markers, and all six 5x5 ball-marker slots. Derive 1008:0b9e status images from strict signed score thresholds rather than unrelated media-award state.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- cargo run --quiet -- --simulate launcher --step 0 --screenshot /tmp/tdkpin-display.png
- git diff --check
2026-08-23 19:19:12 +02:00
ddidderr 0e7208e03a fix(config): import original INI semantics
Load case-insensitive Language, Speed, and singular Sound values from an adjacent or embedded TDKPIN.INI before portable JSON exists, preserving Win16 defaults and clamps. Separate runtime playback from stored settings so F12 remains a silent, non-persistent toggle exactly like the original.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 19:12:31 +02:00
ddidderr fae322bb91 fix(ui): restore original high-score windows
Replace modern overlays with the reconstructed HighScore child and DIALOG 32514 topology, exact DAT993 background, row coordinates, original strings, unpadded scores, and 21-character edit behavior. Restore OK/Cancel semantics, including Escape preserving the prefilled name and allowing an accepted empty name, with deterministic screenshot scenarios.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- cargo run --quiet -- --simulate highscores --step 0 --screenshot /tmp/tdkpin-highscores.png
- cargo run --quiet -- --simulate name-entry --step 0 --screenshot /tmp/tdkpin-name-entry.png
- git diff --check
2026-08-23 19:09:28 +02:00
ddidderr c80f5e2136 fix(ui): restore staged loading progress
Correct raw 1000:531e's Pascal-order BitBlt to destination (202,328), height 13, and width (stage-1)*7 from original DAT994. Render stages 2 through 35 over DAT995, add deterministic loading screenshots, and update the C harness and evidence ledger for the corrected 237x13 source.

Test Plan:
- bash original/tools/test_reconstructed_c.sh
- python3 original/tools/audit_reconstruction.py --require-complete
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md
- cargo run --quiet -- --simulate loading --at 0.55 --screenshot /tmp/tdkpin-loading-mid.png
- git diff --check
2026-08-23 19:03:09 +02:00
ddidderr 8168d9dc23 fix(ui): restore original attract animation
Advance idle mode on the selected detail callback and reproduce the reconstructed target, word-quad, magnetic, record-strip, point-chase, item, and BITMAP500 marquee phases from original assets. Add a deterministic attract screenshot scenario and correct DAT801-809 item placement to y=300.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- cargo run --quiet -- --simulate attract --step 24 --screenshot /tmp/tdkpin-attract-24.png
- cargo run --quiet -- --simulate attract --step 192 --screenshot /tmp/tdkpin-attract-192.png
- cargo run --quiet -- --simulate attract --step 4608 --screenshot /tmp/tdkpin-attract-4608.png
- git diff --check
2026-08-23 18:59:19 +02:00
ddidderr 2936c69c5c fix(physics): preserve first-candidate scan semantics
Carry predicted position separately through exact record-ID chunks, apply magnetic rewrites only at records 6/153/154, and honor type-three/dynamic broadphase. Match raw 1000:c79c by resolving fixed candidate slot one rather than a later nearer record, and restore related capture-age, tilt, and stacked multiball scoring side effects.

Test Plan:
- bash original/tools/test_reconstructed_c.sh
- python3 original/tools/audit_reconstruction.py --require-complete
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md original/MECHANICS_PROGRESS.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md
- git diff --check
2026-08-23 18:50:20 +02:00
ddidderr f8dcceda5e fix(physics): restore binary record scan order
Interleave static ranges, type-three captures, type-four triggers, magnetic records, and dynamic ball records in exact ID order while tracking predicted position separately from mutable motion. Preserve type-three/dynamic broadphase and the raw stack quirk where fixed candidate slot one wins even when a later candidate is nearer; seal that behavior in both C and Rust harnesses.

Test Plan:
- bash original/tools/test_reconstructed_c.sh
- python3 original/tools/audit_reconstruction.py --require-complete
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md original/MECHANICS_PROGRESS.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md
- git diff --check
2026-08-23 18:46:44 +02:00
ddidderr 89880e9b45 fix(physics): process sensors before collision response
Evaluate type-three captures from the pre-movement position, retain their missing radial rim candidates, and run type-four motion randomization before candidate resolution and position publication. Sensor groups now execute in binary ID order without Hold or Complete prematurely aborting later records.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:37:03 +02:00
ddidderr 95b6994735 fix(physics): apply predicted record broadphase
Use unresolved type-one/type-two candidates in the production scan and gate detection on the original predicted-position bounds with the registered five-pixel ball margin. This restores the 1000:9b69 broadphase, including overshoot rejection, while keeping late response resolution ready for ordered type-three/type-four integration.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:29:53 +02:00
ddidderr 975fb27494 refactor(physics): defer static collision responses
Split type-one and type-two detection from response resolution so the retained candidate can be applied to the motion state that exists after the complete record scan. Existing response functions remain compatibility wrappers, and a focused test proves late motion changes participate in resolution.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --check
2026-08-23 18:28:02 +02:00
ddidderr 742b8b7981 fix(physics): use predicted magnetic gate bounds
Correct the readable 1000:9b69 reconstruction to read DGROUP 07db/07df and name the helper for its predicted-position role. Rust magnetic rectangles now enter only from an old position inside the record and deactivate when the recomputed prediction exits any edge, replacing the prior swept approximation.

Test Plan:
- bash original/tools/test_reconstructed_c.sh
- python3 original/tools/audit_reconstruction.py --require-complete
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check original/C_RECONSTRUCTION_FINAL_AUDIT.md tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md tdkpin-rs/README.md
- git diff --check
2026-08-23 18:25:23 +02:00
ddidderr 8c51094b31 fix(timing): decay tilt on detail callbacks
Remove the independent 30 ms tilt accumulator and decrement the wrapping 16-bit counter once at the end of each selected multimedia detail callback. This restores the original 50/40/30/20/10 ms decay cadence across all five settings.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:17:29 +02:00
ddidderr 0661665374 fix(input): restore multiball nudge slot updates
Preserve the Win16 key path's two-stage multiball behavior: consume the active-window random formula, then apply one separately recovered slot impulse to both saved ball velocities. This restores the original RNG advancement and prevents ball one and ball two from receiving invented different nudges.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:16:09 +02:00
ddidderr 3d5a105c10 fix(physics): preserve record layer and kick fields
Carry the recovered +0x49 layer mask through every static line/circle and select the original upper/lower scan layer from the previous Y coordinate. Restore negative auxiliary thresholds and kick coefficients for bumpers and records 55, 74, and 107, including weak-hit scoring, rail sound, and tilt suppression.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:13:35 +02:00
ddidderr 692e0087f9 fix(physics): restore slot-major timer batches
Freeze the active ball count at each detail callback, integrate every substep for slot one before slot two, and stop a slot after its first collision or lifecycle action. Effect-seven spawns are now deferred until the next callback, while a surviving slot two retains its original ball number for the remainder of a callback after slot one exits.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:08:21 +02:00
ddidderr 445af4a030 fix(game): dispatch sensors for both ball slots
Store capture progression with each recovered ball slot and pass the original one-based ball number through type-3 and type-4 handling. Multiball captures now remove only the captured slot, promote the survivor when slot one exits, and preserve the effect-seven argument guard that prevents ball two from spawning a third ball.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 18:04:30 +02:00
ddidderr bee3c440bf fix(game): restore effect-seven multiball spawn
Use the exact record 148 spawn point and the reconstructed 0.8 * 3800 millipixel velocity for ball slot two. This removes the guessed shooter-lane launch and keeps the multiball lifecycle tied to the recovered effect-seven path.

Test Plan:
- cargo test --all-targets
- cargo clippy --all-targets --all-features -- -D warnings
- rumdl check CHANGELOG.md RECONSTRUCTION.md README.md
- git diff --check
2026-08-23 17:58:37 +02:00
ddidderr f8292dc87e fix(render): restore timed record overlays
Replace the shared bumper cooldown and synthetic circle outlines with the
original per-record countdown bytes. Bumpers 51-53 use five active callbacks,
side targets 140-147 use twenty, and top targets 150-152 use ten; countdowns
pause while the ball is idle and decrement once per active detail callback.

Render each active record by copying its exact inclusive binary render bounds
from overlay A at the same destination, then naturally fall back to overlay B
when the countdown reaches zero. This removes invented vector effects and keeps
visual lifetime tied to gameplay timer state.

Test Plan:
- `cargo test --all-targets` -- passed, 75 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check CHANGELOG.md RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:55:59 +02:00
ddidderr 096d923c79 fix(game): restore six-state target rotation
Replace the free-running 68x60 wheel animation with the original six-callback
91x90 DAT600 mechanism. Start WAVE 2011 at state one, advance once per selected
detail timer, and rotate the five per-player lock contact/item values exactly at
state six.

Decode the 17x17 DAT600 target image/mask pair and render all six recovered
position sets over the correct atlas or overlay-B base. Preserve filled targets
in their resting state after rotation and add a deterministic `targets` scenario
for trace and framebuffer validation.

Test Plan:
- `cargo test --all-targets` -- passed, 74 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check CHANGELOG.md README.md RECONSTRUCTION.md` -- passed
- rendered all six target-rotation states -- visually verified
- `git diff --cached --check` -- passed
2026-08-23 17:53:40 +02:00
ddidderr 4b84460c93 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
2026-08-23 17:49:04 +02:00
ddidderr 6337291461 fix(ui): preserve original idle view and function keys
Remove the invented attract-screen instruction panel so idle mode presents the
original table artwork without a modern overlay. Keep the original plus-key
start flow and automatic high-score presentation.

Stop assigning F2 and F3 to modern screens; those keys are normally inactive in
the original executable outside dormant debug paths. Retain optional portable
settings and high-score viewers on F10 and F9, documenting them as additional
host controls rather than original behavior.

Test Plan:
- `cargo test --all-targets` -- passed, 70 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check CHANGELOG.md README.md RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:43:44 +02:00
ddidderr 2549eaa459 fix(timing): batch state on original detail callbacks
Replace the host-frame 10 ms publication loop with the original timer matrix:
50/40/30/20/10 ms callbacks execute exactly 5/4/3/2/1 internal physics
substeps. Ball state, claw progression, and flipper geometry are now visible only
at the selected callback boundary rather than leaking intermediate substeps.

Separate flipper input flags from flipper position. Key changes are sampled by
the timer, then produce one delta edge, WAVE 2021, collision response, and
rendered position update after physics, matching the Win16 callback order. Clear
both input and position state on turn reset and keep tilt decay on the same timer
cadence.

Test Plan:
- `cargo test --all-targets` -- passed, 70 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check CHANGELOG.md README.md RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:41:18 +02:00
ddidderr 1f0085902c fix(physics): port the Borland Real48 collision core
Add a bit-exact six-byte Real48 implementation for integer conversion,
rounding, comparison, add/subtract, multiply/divide, and Newton square root.
Use the normalized Borland random-register result and route speed clamps,
segment/circle detection, moving flippers, captures, triggers, and magnetic
fields through the recovered arithmetic instead of host floating formulas.

Preserve Real48 spin per ball and feed it through the original tangent/spin
response, separating zero-spin C fixtures from retained-spin Wine traces.
Replace path-progress selection with surface-distance ordering and implement
dynamic records 174/175, including impulse transfer to the other slot,
normal_velocity-1000 response, and the second post-collision speed clamp.

Test Plan:
- `cargo test --all-targets` -- passed, 69 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:36:03 +02:00
ddidderr 98f21b31c0 fix(physics): restore type-three contact state
Replace the shared boolean sensor latch with the two distinct states used by the
binary: per-player 16-bit contact words for type-three records and transient
entry flags for type-four triggers. Only active/contact record state is mirrored
across player turns, matching the original save/load loop.

Port the type-three deep-inside threshold, velocity damping and 150-millipixel
pull, stationary gate, first-contact sound and ball-number publication, shared
capture age in five-unit steps, and the 99/2 completion sentinels. Lock holes and
the reset pocket now complete after the recovered hold interval; the claw
publishes its sound on contact and starts on the following completed state.

Test Plan:
- `cargo test --all-targets` -- passed, 63 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:18:20 +02:00
ddidderr d72db79af6 fix(physics): replace fitted flipper transfer
Remove the live-fitted upward and downward flipper polynomials. Port the
reconstructed 1000:7ed9 path with delta-specific pivots and record edges,
integer cross-product and radius gates, penetration, response-record gain,
wrapping velocity updates, and the final position delta.

Use resting records on press and the already-raised records on release, correct
the original delta direction, and apply each edge to both live ball slots. Keep
render-facing Vec2 projections, but adjust their float representation by ULPs so
every gameplay millipixel round-trips exactly instead of losing reconstructed
integer results.

Test Plan:
- `bash original/tools/test_reconstructed_c.sh` -- passed during raised-record reference capture
- `cargo test --all-targets` -- passed, 62 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:11:48 +02:00
ddidderr c1d8e5755f fix(input): restore launcher press and release impulses
The rewrite used a 375-millipixel impulse for both charging and release. The
reconstructed key handlers use the setup scalar 15: every repeated Down keydown
subtracts 15*50, and release subtracts a further 15*100 before clamping.

Use 750-millipixel press increments and the 1,500-millipixel release impulse,
then reproduce the randomized -3800 lower clamp and weak -2280 upper branch.
Derive the ten launcher decoration frames from the original strict multiples of
750 and keep integration/gravity assertions separate from the immediate release
state.

Test Plan:
- `cargo test --all-targets` -- passed, 60 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:03:35 +02:00
ddidderr 2f7e29b6a4 fix(physics): restore type-four trigger response
The rewrite scored type-four targets but left ball velocity unchanged. The
reconstructed collision loop consumes two Borland random values after each
entered radius-bearing trigger and scales X and Y independently by
1.03 - Random * 0.08.

Apply that response to targets 140 through 152 and effect record 149 after their
ordered rule handling. This restores both the immediate trajectory and the
shared random-stream position used by later launcher, claw, nudge, and mechanism
choices.

Test Plan:
- `cargo test --all-targets` -- passed, 60 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:01:44 +02:00
ddidderr 3fa3e29ad3 fix(scores): restore per-player high-score flow
The rewrite waited for the whole game to finish, selected only the maximum
player score, and sorted entries as ordinary unsigned integers. The original
checks each player immediately when their own last ball is lost and compares
scores by signed high word then unsigned low word.

Emit a candidate at each player elimination, prompt qualifying players with the
original `TDK Pinball Player` value, show the table, and then either resume the
next player or return to attract mode. Preserve source table order on import and
insert only strictly qualifying scores with the original word-wise comparator;
keep JSON as the host persistence representation.

Test Plan:
- `cargo test --all-targets` -- passed, 60 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 17:00:12 +02:00
ddidderr 64d41eb605 fix(game): preserve rule state across player turns
The rewrite reset wheel holes, top targets, collision latches, active records,
selected effects, and multiball readiness on every drain. The reconstructed
turn-state code instead saves all 175 live record states for the outgoing player
and restores the incoming player's mirror.

Add a per-player rule snapshot and save/load it at normal turn transitions.
Keep transient ball, claw, tilt, and presentation state global while retaining
each player's mechanism progress and contact/activation state across later
turns and extra balls.

Test Plan:
- `cargo test --all-targets` -- passed, 58 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 16:58:10 +02:00
ddidderr 0c52100a7e fix(game): restore per-player secondary scoring
Replace the global bonus approximation with the original per-player secondary
score and multiplier. Lock holes now accumulate 10k, 20k, 40k, 80k, and 160k;
the fifth transfer applies the current multiplier, clears the secondary score,
increments that multiplier, and grants the recovered extra-ball count.

Route effects 1 through 5 into the same secondary score and make effect 6
transfer its displayed multiplied value before clearing it. Render that value
on the table and stop adding an unrelated global bonus during drain, preserving
secondary progress across balls as the original player state does.

Test Plan:
- `cargo test --all-targets` -- passed, 57 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 16:56:59 +02:00
ddidderr aae4671586 fix(input): restore original nudge and tilt behavior
Replace the float nudge direction, cooldown, and decaying meter with the
scan-code-specific Win16 behavior. Left Shift and keypad 3 now use the original
random directional impulse, while Space uses the reconstructed random horizontal
and vertical formulas with scalar 15 and wrapping millipixel velocities.

Track the original 16-bit tilt counter: each nudge adds 25, compares against
30 + Random(10), and the selected detail timer decrements a nonzero counter once
per callback. Allow the same input while a ball waits in the launcher, retain
bonus state when tilt latches, clear flippers immediately, and keep Right Shift
only as a documented modern alias for keypad 3.

Test Plan:
- `cargo test --all-targets` -- passed, 57 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check tdkpin-rs/CHANGELOG.md tdkpin-rs/README.md tdkpin-rs/RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 16:55:02 +02:00
ddidderr c25287186a fix(game): use the original Borland random stream
Replace the Xorshift state with the reconstructed Borland Win16 generator,
including its wrapping linear update and high-word Random(n) mapping. Route
launcher clamp variation, effect selection, and claw terminal choices through
one shared stream so seeded event order follows the executable.

Use the same stream for the recovered magnetic-field response. Fields now scale
horizontal speed by 0.9 and choose the upward impulse from
-3800 * (1 - Random * 0.3) instead of assigning a fixed -3000 velocity. Adjust
deterministic scenario expectations where the original stream permits a second
natural claw capture or does not force one during a particular autoplay seed.

Test Plan:
- `cargo test --all-targets` -- passed, 56 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check CHANGELOG.md RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 16:52:17 +02:00
ddidderr 9de3dcc755 fix(game): restore original scoring and sound dispatch
The rewrite assigned one WAV resource to each logical event, which could not
represent the original call-site behavior. It played reserved WAV 2022 for
media awards, omitted multi-sound bank sequences, and used the wrong cues for
captures, bumpers, targets, nudges, and tilt.

Emit explicit sound-resource events at the reconstructed C control-flow sites
so logical rendering events no longer imply audio. Move media threshold
advancement into score addition, retain one threshold transition per call, and
use wrapping 32-bit score arithmetic. Restore the ninth-diamond 24,464-point
completion, the subsequent double-score activation, and later 100,000-point
secondary-score awards.

Test Plan:
- `cargo test --all-targets` -- passed, 53 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `rumdl check CHANGELOG.md RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
2026-08-23 16:48:41 +02:00
ddidderr 8b99e9607c feat(reconstruction): complete binary-backed C recovery
Replace the partial mechanics transcriptions with a separate, readable C11
reconstruction of the complete Win16 image while preserving the original raw
Ghidra export as immutable evidence. Cover all ordinary and overlapping entry
points, Borland runtime behavior, Win16 imports, segmented data, callbacks,
resources, indirect control flow, physics, rendering, persistence, and
startup/shutdown lifecycles.

Add deterministic extraction and audit tooling plus address-linked ledgers for
functions, imports, DGROUP ranges and objects, relocations, resources, and
callbacks. The final gate records zero raw, partial, restored, unknown,
blocked, or unclassified required units. Keep the semantic-fidelity boundary
explicit: the portable C is not claimed to reproduce a byte-identical Borland
NE build.

Add strict focused harnesses for every reconstructed C unit, exact resource
round-trip checks, and a 16-bit Borland Real48 reference probe. No Rust source
or Cargo metadata is changed in this phase.

Test Plan:
- `bash original/tools/test_reconstructed_c.sh` -- passed
- `bash original/tools/probe_real48_reference.sh` -- passed bit-for-bit
- `python3 original/tools/audit_reconstruction.py --require-complete` -- passed
- `git diff --cached --check` -- passed
- `git diff HEAD -- '*.rs' Cargo.toml Cargo.lock` -- empty
2026-08-23 16:41:17 +02:00
ddidderr aef404c834 fix(ui): restore original presentation scale
The recovered client enlarged the 640x460 canvas to 960x690 and shrank the
score glyphs, so artwork and numeric displays no longer matched the original
pixel presentation. Restore the native window dimensions and title, render
score digits at their recovered 16x28 size, and align the score fields to the
original layout. Update the user and reconstruction documentation accordingly.

Test Plan:
- `cargo test --workspace --all-targets --all-features` -- passed, 52 tests
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- passed
- `rumdl check --flavor commonmark CHANGELOG.md README.md RECONSTRUCTION.md` -- passed
- `git diff --cached --check` -- passed
- `cargo +nightly fmt --all -- --check` -- baseline fails on existing import formatting in touched and untouched Rust files
- `prettier --check CHANGELOG.md README.md RECONSTRUCTION.md --prose-wrap always --print-width 80` -- baseline fails on existing whole-file formatting in README.md and RECONSTRUCTION.md
2026-08-23 06:08:11 +02:00
ddidderr a70331fb8c docs(reconstruction): close gameplay fidelity ledger
Build TDK Pinball / build (macos-latest) (push) Canceled after 0s
Build TDK Pinball / build (ubuntu-latest) (push) Canceled after 0s
Build TDK Pinball / build (windows-latest) (push) Canceled after 0s
Update the evidence boundary after fixed-point contacts, complete rule/scoring
routes, magnetic fields, all claw terminals, and effect-seven multiball were
restored. Distinguish covered gameplay behavior from the remaining intentional
source/platform differences: live-fitted flipper arithmetic and Win16 repaint
batching.

Test Plan:
- final 52-test, strict Clippy, production, and cross-target suite -- passed
- 63/63 raw resources and every decoded derivative -- verified
- restored physics C strict C11 syntax -- passed
- two-minute autoplay and real-window keyboard smoke run -- passed
- `git diff --cached --check` -- passed
2026-08-22 22:04:46 +02:00
ddidderr f2560ee2f7 fix(game): restore effect-seven multiball
Arm effect 7 after the wheel-reset target, select it on the next effect line,
and spawn a second visible live ball when record 149 is consumed. Advance the
second ball through recovered line, circle, ball-ball, magnetic, and scoring
collisions independently.

During multiball, draining one ball leaves the other active without consuming a
remaining player ball. Include secondary state in deterministic traces and
finite-state validation.

Test Plan:
- `cargo test --all-targets` -- 52 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- production build plus Windows GNU and macOS checks -- passed
- effect-seven spawn, movement, and single-drain lifecycle test -- passed
- `git diff --cached --check` -- passed
2026-08-22 22:02:50 +02:00
ddidderr 25e32b2c06 fix(game): honor original mechanism timer choices
Map detail levels 1-5 to the recovered 50, 40, 30, 20, and 10 ms callback
periods for claw animation while retaining the invariant 10 ms physics step.
This removes the hard-coded default cadence from non-default configurations.

Keep repaint batching documented separately because Rust exposes intermediate
physics states instead of reproducing Win16 GDI callback batching.

Test Plan:
- `cargo test --all-targets` -- 51 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- all five claw cadence boundary tests -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:55:21 +02:00
ddidderr 77c93ec36b fix(game): restore exact media thresholds
Replace the inferred exponential media progression with the four initialized
32-bit thresholds used by the original score-addition path: 140,000, 650,000,
1,300,000, and 4,000,000. Award exactly one media level and extra ball at each,
with no nonexistent fifth tier.

Test Plan:
- `cargo test --all-targets` -- 50 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- threshold boundary and four-extra-ball test -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:53:11 +02:00
ddidderr b786a75544 fix(game): restore the six-way effect target
Implement the flagged object-84 selector and initially inactive type-4 object
149. Choose a different effect from 1-6, render the enabled target from its
original active-table rectangle, award its static 500 points, then apply the
recovered bonus or cashout action and disable it until reselected.

This replaces the final unimplemented help-screen target effect with its
original dispatcher values.

Test Plan:
- `cargo test --all-targets` -- 49 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- live object-84 probe activated record 149 and selected effect 3
- effect selection/consumption test -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:50:50 +02:00
ddidderr 27749cb5f8 fix(game): correct every claw release velocity
Interpret the claw's 3800-unit release scalar per 10 ms physics substep rather
than per 30 ms animation frame. This removes a factor-of-three slowdown from all
four random terminals and restores terminal 18 to raw `(0,1000)`.

Add exact assertions for positions and raw velocities of terminals 1, 6, 7,
and 18, backed by repeated live original captures through release and the first
three gravity substeps.

Test Plan:
- `cargo test --all-targets` -- 48 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- all four live original terminal trajectories matched
- `git diff --cached --check` -- passed
2026-08-22 21:46:07 +02:00
ddidderr 4b56a23fb4 fix(game): restore one-shot magnetic fields
Replace the global timed magnet and drain teleport with the three recovered
field records. Map top targets 150-152 to center field 6 and outlane fields
153-154, apply upward pull only inside each registered rectangle, and deactivate
the field after it pulls the ball through its upper boundary.

Render each active field from its exact original active-table rectangle instead
of drawing invented yellow triangles.

Test Plan:
- `cargo test --all-targets` -- 48 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- production build plus Windows GNU and macOS checks -- passed
- live field-153 entry direction matched and Rust field lifecycle test passed
- `git diff --cached --check` -- passed
2026-08-22 21:43:13 +02:00
ddidderr b86810ebed fix(physics): restore the flipper downstroke
Represent press and release as distinct deferred flipper edges. Keep upward
position/velocity transfer unchanged, and apply the position-preserving
return-stroke velocity recovered from a live ball at `(125,390)`, mirrored for
the right flipper.

The downstroke now assigns raw `(3519,6302)` before the next physics clamp,
matching the original instead of moving geometry silently.

Test Plan:
- `cargo test --all-targets` -- 48 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- live downstroke transfer test -- passed within one raw unit
- `git diff --cached --check` -- passed
2026-08-22 21:36:24 +02:00
ddidderr 7d28818e23 test(game): exercise two minutes of autoplay
Add a seeded autoplay scenario that performs a deliberate full-strength charge
for every launcher ball and operates both flippers from live ball position.
Turn the two-minute run into an end-to-end test covering repeated launches,
flipper edges, bumpers, targets, lock holes, claw capture/release, and drains
while rejecting non-finite state.

Document autoplay as the long-run validation entry point and record the expanded
runtime coverage boundary.

Test Plan:
- `cargo test --all-targets` -- 48 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- Windows GNU and macOS x86_64 `cargo check` -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:33:29 +02:00
ddidderr cd27fc8404 docs(reverse): transcribe the fixed-point physics core
Add a standalone, syntax-checked C transcription of the recovered ball state,
10 ms substep, launcher repeat/release arithmetic, one-sided type-2 response,
swept type-1 response, and previous-position contact application. Link the
source to the authoritative raw function addresses and initialized object
ledger.

Keep unresolved persistent-contact and special-rule branches in the raw Ghidra
artifact rather than filling the readable reference with guesses.

Test Plan:
- strict C11 `clang -Wall -Wextra -Werror -fsyntax-only` -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:29:41 +02:00
ddidderr 5e63772967 fix(game): restore wheel locks and reset target
Replace the inferred eight-sector wheel and central lock rectangle with the six
recovered type-3 records. Fill the five lock holes at their exact centers,
double each successive bonus from 10,000 through 160,000, transfer the complete
310,000 on the fifth, and return a launcher ball without consuming one.

Restore object 148 as the wheel-reset target and clear all filled-hole state on
contact. Keep these sensors on the same swept path and contact-latch machinery
as the claw and type-4 targets.

Test Plan:
- `cargo test --all-targets` -- 47 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- live five-hole bonus/score sequence matched exactly
- live object-148 reset cleared all five contact states
- `git diff --cached --check` -- passed
2026-08-22 21:26:20 +02:00
ddidderr 1ca8597080 fix(game): restore physical target-bank rules
Attach recovered score and rule flags to type-2 collision records. Deactivate
complete three-line groups through their contact owner, rearm all five
bumper-value groups after completion, and rearm all four central groups after
advancing the TDK diamond.

Remove the broad side and colored-target coordinate checks those physical
records replace. Preserve exact 5,000, 100,000, bank, and wheel-trigger scores
from the initialized object ledger.

Test Plan:
- `cargo test --all-targets` -- 46 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- target-bank deactivation, completion, and rearm test -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:20:15 +02:00
ddidderr 20e751de80 fix(physics): resolve the earliest substep contact
Return exact path progress with each fixed-point line and circle candidate.
Evaluate the complete recovered physical object set and apply the nearest
contact, matching the selection logic in the original collision dispatcher
instead of stopping at the lowest matching object id.

This prevents dense assemblies from choosing a later wall or circle merely
because its record appears earlier in the table.

Test Plan:
- `cargo test --all-targets` -- 45 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- collision-progress ordering test -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:15:45 +02:00
ddidderr bf5b855f44 fix(game): restore swept target sensors
Move claw contact and all eleven initially active type-4 circle records onto a
swept millipixel sensor test with per-object enter latching. Preserve the exact
left-bank and top-gate coordinates, radii, and scores from the initialized
object ledger.

Remove the inferred top-bank completion rule. Each original top target now
scores 500 and independently starts recovered magnetic-gate timing instead of
requiring all three for an invented bonus.

Test Plan:
- `cargo test --all-targets` -- 44 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- top sensor contact-latch and re-entry test -- passed
- 20-second deterministic launch remained in active table motion
- `git diff --cached --check` -- passed
2026-08-22 21:12:41 +02:00
ddidderr 9d91cc51c7 feat(reverse): ledger every initialized game object
Add a reproducible dump of all 175 initialized collision and rule records from
the unmodified traceable Wine process. Preserve geometry, bounds, activation,
all four decoded Borland Real48 response values, flags, contact state, score,
layer mask, and render rectangle in a reviewable TSV.

This extends the static function ledger with the runtime-built data table needed
to replace inferred target, lock, gate, and mechanism logic in Rust.

Test Plan:
- generated `OBJECTS.tsv` contains one header plus 175 records
- repeat dump compared byte-for-byte equal with `cmp`
- `ruff check` on all original tracing tools -- passed
- `git diff --cached --check` -- passed
2026-08-22 21:07:18 +02:00
ddidderr d813b5b9da fix(physics): restore upward flipper transfer
Apply flipper movement after the physics substep, matching the original timer
order. Replace the weak generic tangent kick with the recovered swept-body
position transfer and pre-clamp velocity curve fitted from three live left-edge
probes, then mirror that local transform for the right flipper.

The x=125 probe now reaches `(138.182,355)` with raw velocity within two units
of `(2217,-5889)`. Keep downstroke interaction listed as pending rather than
claiming the upward fit is the complete raw routine.

Test Plan:
- `cargo test --all-targets` -- 43 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- three live left-flipper transfer probes matched within documented tolerance
- `git diff --cached --check` -- passed
2026-08-22 21:05:02 +02:00
ddidderr e07a915277 fix(physics): restore live flipper geometry
Replace each invented centerline capsule with the original pair of moving line
records and moving tip circle. Use the exact asymmetric raised coordinates read
from live objects 66-68 and 81-83, while preserving their recovered type-1 and
type-2 fixed-point response coefficients in normal object traversal.

Remove the last generic segment overlap solver. Edge-time ball transfer remains
separate because the original applies it after physics while moving the records.

Test Plan:
- `cargo test --all-targets` -- 43 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- live left and right key-edge record dumps matched the Rust coordinates
- `git diff --cached --check` -- passed
2026-08-22 21:00:22 +02:00
ddidderr c886a93788 fix(physics): restore fixed-point circle responses
Replace the overlap-and-separate circle approximation with the original swept
ball-center entry test and radial/tangent fixed-point response. Preserve every
type-1 record's Real48 rebound and tangent coefficients, plus the bumper normal
kick derived from the 3800-unit speed scalar.

Unify line and circle traversal in original object-id order and remove the old
floating passive-circle and bumper impulses. Add exact tests for live object 155
and bumper 51 transitions, and keep moving flippers as the explicit remaining
collision approximation.

Test Plan:
- `cargo test --all-targets` -- 44 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- 20-second deterministic launch traversed bumpers and drained without rescue
- live object 155 and bumper 51 fixed-point transitions matched exactly
- `git diff --cached --check` -- passed
2026-08-22 20:52:58 +02:00
ddidderr 5c2a80cfa5 fix(reverse): avoid stopping Wine during probes
Repeated SIGSTOP-based injection can suspend Wine while its Win16 mutex is held
and eventually strand the timer callback. Make the short process-memory write
sequence the default between 30 ms callbacks, while retaining whole-process
stopping as an explicit single-probe diagnostic option.

Test Plan:
- `ruff check original/tools/trace_original_state.py original/tools/inject_original_state.py` -- passed
- live no-stop ball-state injection -- passed
- subsequent original timer updates remained observable
- `git diff --cached --check` -- passed
2026-08-22 20:52:03 +02:00
ddidderr 7caf460022 fix(physics): restore fixed-point rail movement
Replace the guessed variable-rate floating integration with the original 10 ms
millipixel step, 15-unit gravity, and 3800-unit speed limit. Restore every
registered type-2 Real48 response pair, one-sided start-to-end orientation, and
the original normal/tangent velocity transform validated against live shooter
wall traces.

Restore repeat-driven plunger charging and its release-time randomized clamp.
The shooter route now follows table orientation instead of coordinate-specific
gate exceptions. Remove the synthetic ball-search impulse so future stuck balls
remain visible reconstruction failures, and include collision ids in deterministic
traces for differential work on circles and triggers.

Test Plan:
- `cargo test --all-targets` -- 42 passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- charged launcher scenario cleared the shooter lane -- passed
- all recovered rails accepted front-side and rejected back-side probes
- `git diff --cached --check` -- passed
2026-08-22 20:42:47 +02:00
ddidderr c50ed39a35 feat(reverse): isolate original physics substeps
Expose the original detail-derived integration count, active-ball count,
gravity, maximum speed, and movement flags in live traces. Allow probes to
force one physics substep so a single collision response can be observed
without the default three-step timer batch obscuring intermediate state.

Record the dynamically verified type-2 rail response: resolve velocity in the
registered line's normal/tangent basis, apply the two original Real48
coefficients, then advance from the previous position using that response.

Test Plan:
- `ruff check original/tools/trace_original_state.py original/tools/inject_original_state.py` -- passed
- one-substep probes against both vertical shooter walls -- passed
- ordinary rail responses matched 0.6 normal rebound and 0.1 tangent coupling
- `git diff --cached --check` -- passed
2026-08-22 20:27:56 +02:00
ddidderr 65ee3c5355 fix(reverse): trace active injected ball states
The tracer could only rediscover the game object while all original position
copies agreed, which is normally true at the waiting launcher but not during an
active controlled probe. Accept the injector-reported object base explicitly so
one process can be sampled through an entire sequence of collision tests.

Test Plan:
- `ruff check original/tools/trace_original_state.py original/tools/inject_original_state.py` -- passed
- active live trace with `--object-base 0xadc2b0` -- passed
- `git diff --cached --check` -- passed
2026-08-22 20:23:06 +02:00
ddidderr d24ef6612f feat(reverse): inject controlled original ball states
Add an atomic live-state injector for the traceable Wine process. It updates
all recovered global and object copies of ball position and velocity while the
process is stopped, clears pending impulses, and re-enables normal collision
processing before resuming the unmodified executable.

This makes individual walls, circles, gates, and mechanisms reachable without
relying on random gameplay. Document the first controlled claw probe, including
its original terminal-18 animation, exact release state, and three-substep
gravity behavior.

Test Plan:
- `ruff check original/tools/trace_original_state.py original/tools/inject_original_state.py` -- passed
- controlled `(289,94)` injection and one-second live claw trace -- passed
- terminal-18 release matched `(325000,92000)` and `(0,1000)` -- passed
- `shellcheck original/tools/run_traceable_original.sh` -- passed
- `git diff --cached --check` -- passed
2026-08-22 20:20:26 +02:00
ddidderr dcc664346c feat(reverse): trace the live original physics state
WineDbg changes the Win16 exception path and cannot capture continuous game
state reliably. Add a Linux-only launcher that opts the normally running Wine
process into same-user tracing without modifying TDKPIN.EXE, plus a sampler
that locates the loaded DGROUP and game object from recovered signatures.

The CSV trace exposes original millipixel positions, predicted positions,
velocities, flipper state, and claw state whenever they change. This provides
an external oracle for reconstructing and differentially testing the fixed-
point mechanics instead of tuning the Rust approximation against screenshots.

Test Plan:
- `shellcheck original/tools/run_traceable_original.sh` -- passed
- `ruff check original/tools/trace_original_state.py` -- passed
- strict shared-library compile with `cc` warnings as errors -- passed
- live Wine smoke trace and four-second charged launch capture -- passed
- `git diff --cached --check` -- passed
2026-08-22 20:16:14 +02:00
ddidderr 9e74745fde docs(testing): record live original comparison
Document the Wine-backed original start and charged-launch captures, the matching deterministic Rust states, and the remaining boundary that a chosen claw terminal was not forced in the live original.

Test Plan:
- Launch original/TDKPIN.EXE with Wine 11.15 staging
- Send keypad plus, hold Down for 1.1 seconds, release, and inspect captures
- Compare original started state with Rust launcher step zero
- git diff --cached --check
2026-08-22 18:55:41 +02:00
ddidderr 744e9e4fc1 test(physics): exercise every recovered collider
Probe both contact sides of all 109 recovered wall segments and every passive/scored circle. Verify each primitive detects overlap, separates the ball, and rejects inward maximum-speed motion.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --cached --check
2026-08-22 18:55:04 +02:00
ddidderr d020de613c fix(physics): kick only on flipper movement edges
Model the recovered move_flipper call boundary: raising sweeps the occupied region once and applies one tangential impulse, while a held flipper remains solid without accumulating another boost on every collision substep.

Returning still updates geometry and emits its movement sound but cannot kick the ball.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --cached --check
2026-08-22 18:53:03 +02:00
ddidderr b7326f7a13 fix(input): require plus to start players
Remove the Enter shortcut so the start flow matches the original plus-key handler. Use the recovered player-add sound resource for both the first and subsequent players.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --cached --check
2026-08-22 18:50:55 +02:00
ddidderr 16c055eec6 feat(testing): add deterministic mechanics capture
Add named launcher, flipper, and claw scenarios that advance at exact 120 Hz steps with stable seeds. Emit per-step JSON state/event traces and optionally capture the logical 640x460 framebuffer at any requested time or step.

Cover parser behavior, deterministic replay, launcher timing, flipper edges, and complete capture/release/idle cycles for all claw terminals.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- cargo run -- --simulate claw-6 --at 0.15 --screenshot /tmp/tdkpin-sim-validation/claw-6-after-flush.png --trace /tmp/tdkpin-sim-validation/claw-6.json
- Inspect both generated 640x460 PNGs and JSON traces
- git diff --cached --check
2026-08-22 18:50:25 +02:00
ddidderr 75c2464e72 docs(reverse): decode claw release arithmetic
Correct the coordinate model from guessed 16.16 values to the millipixel arithmetic shown by the 16-bit division routine.

Decode the Borland Real48 direction constants and record the exact position and velocity tables used by every claw terminal.

Test Plan:
- git diff --cached --check
- Compare constants against segment 1 disassembly at 9bca and eab1
2026-08-22 18:44:50 +02:00
ddidderr 99a6082b2a fix(game): restore the original claw sequence
Replace the broad robot bounce rectangle with the recovered object-89 capture state machine. Suspend the live ball, advance the exact sprite rows, release from decoded original coordinates and directions, and keep the surrounding collision records disabled until the arm returns home.

Render the 900x296 sheet as 100x74 frames at the original destination and suppress the duplicate ball while it is embedded in the capture animation.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --cached --check
2026-08-22 18:44:09 +02:00
ddidderr 445bb22fb4 fix(audio): restore recovered mechanism sounds
Play the original WAVE resource for both movement edges of each flipper and centralize gameplay event sound selection.

Replace guessed control and mechanism mappings with the resource numbers recovered from the Win16 call sites. Keep synthetic ball search silent because it has no original trigger.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --cached --check
2026-08-22 18:39:29 +02:00
ddidderr e1dc644c76 docs(reverse): restore named mechanism state machines
Transcribe the claw and flipper control flow into an address-mapped readable companion while preserving unresolved fixed-point projection as an explicit boundary.

Correct the reconstruction ledger so the current Rust claw is not described as proven parity.

Test Plan:
- git diff --cached --check
- Review restored constants and branches against TDKPIN_GHIDRA_RAW.c
2026-08-22 18:38:07 +02:00