Commit Graph
54 Commits
Author SHA1 Message Date
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
ddidderr 95760b9940 fix(input): start games with the plus key
Match the recovered Win16 registration flow: the first plus press creates Player 1 and additional presses add Players 2 through 4 until the initial launch. Down now belongs only to charging and releasing the plunger.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- live: verify Down cannot leave attract mode
- live: start with plus, add Player 2, hold Down through partial/full plunger frames, and release onto the table
- git diff --check
2026-08-22 18:24:29 +02:00
ddidderr 8d76e9539f fix(game): restore deliberate plunger charging
Advance the recovered eleven-frame plunger over a full one-second hold and use that same frame selection for rendering. This makes a short tap visibly and physically weaker while a complete pull still clears the shooter lane.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --check
2026-08-22 18:22:24 +02:00
ddidderr 4ef563361b fix(physics): restore recovered flipper states
Replace the reversed oversized flipper vectors with the original binary rest
and raised geometry. Render the exact resource 997 flipper rectangles while
pressed and keep the live collision capsules aligned with those sprites.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- compare forced raised-state rendering with the extracted resource rectangles
2026-08-22 18:15:38 +02:00
ddidderr bb53f88fb3 fix(physics): restore the one-way shooter exit
Use the recovered rail rebound coefficient and let a launched ball cross object
22 in the exit direction after the upper curve. Strengthen the launch
regression so a momentary lane crossing cannot hide a later rebound.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- verify the charged launch trajectory at detail levels 1 through 5
2026-08-22 18:10:55 +02:00
ddidderr aeae63aabe fix(render): restore original game presentation
Compose the running table from the recovered resting bitmap and exact active
UI regions. Remove the procedural replacements that distorted lamps,
flippers, player rows, and display values, and place the plunger strip at its
recovered destination.

Test Plan:
- cargo fmt --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- launch the game and compare the initial frame and charged plunger visually
2026-08-22 18:07:12 +02:00
ddidderr 91ae8cdfd8 fix(game): restore the original plunger launch
Start each ball at the recovered right-lane coordinates and charge the
plunger while Down is held, firing only on release. Render all eleven frames
of resource 901, preserve object 25 as the returning-ball catch, and use the
object table's center-contact extents without adding the art radius twice.

Test Plan:
- cargo fmt --all -- --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- runtime smoke: start, hold Down, release, and inspect the right shooter lane
2026-08-22 17:37:28 +02:00
ddidderr f904977ae4 fix(game): restore the recovered collision table
Replace the mirrored, partial wall transcription with all active static line
and circle objects from the Win16 registration table. Preserve the original
object IDs for drain and shooter-stop handling, and use bounded physics
substeps so fast balls cannot skip thin rails.

Test Plan:
- cargo fmt --all -- --check
- cargo test
- cargo clippy --all-targets --all-features -- -D warnings
- git diff --check
2026-08-22 17:27:34 +02:00
ddidderr 4bc370be12 fix(game): ignore nudges while waiting to launch
A cabinet nudge could still raise the tilt meter after a drain while the next
ball was parked in the launcher. If that latched TILT, launching was correctly
disabled but no active ball remained to drain and clear it, creating a new
softlock.

Only accept nudges for an active ball. This keeps the latched tilt rules intact
while guaranteeing every waiting ball can be launched.

Test Plan:
- `cargo fmt -- --check` -- passed
- `cargo test --all-targets` -- passed, including the waiting-ball regression
- `cargo clippy --all-targets -- -D warnings` -- passed
- `git diff --check` -- passed
2026-08-22 17:06:01 +02:00
ddidderr e815391980 fix(ui): preserve the original canvas and display styling
High-DPI sizing and a resizable client allowed the 640x460 canvas to be placed
inside a much taller window, producing the large internal black band captured in
the gameplay recording. Open an aspect-correct fixed 960x690 client with high-DPI
framebuffer scaling disabled, giving every platform a predictable 1.5x canvas.

Use exact active-table patches for the cyan score fields and derive transparent
numeral glyphs from the recovered Win16 bitmap font. Fill the complete display
slots, move the KBytes readout clear of its border, and place shortcuts on a
readable footer so no dynamic text is clipped or left over an inactive panel.

Test Plan:
- `cargo fmt -- --check` -- passed
- `cargo test --all-targets` -- passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- launched the Linux window and confirmed a 960x690 client with no letterbox,
  full-width cyan fields, recovered score digits, and readable footer -- passed
- `git diff --check` -- passed
2026-08-22 17:03:46 +02:00
ddidderr cf2c839f83 fix(render): restore animated playfield artwork
The gameplay renderer drew the inactive table and then placed thin line flippers
and procedural circles over baked resting artwork. This left two flipper poses
visible at once, made the ball difficult to see, and ignored recovered animation
resources already embedded with the reconstruction.

Build an alpha-masked texture from the original ball bitmap and mask, erase the
baked flipper pose before drawing collision-aligned moving flippers, and animate
the recovered wheel, robot, and magnet sheets from their gameplay events. Add
bumper flashes, exact lit-table pixels for TILT, and a short visual table shake
for nudges so contacts and machine state have immediate feedback.

Test Plan:
- `cargo fmt -- --check` -- passed
- `cargo test --all-targets` -- passed
- `cargo clippy --all-targets -- -D warnings` -- passed
- launched the Linux window and inspected attract, gameplay, resting flippers,
  raised flipper cleanup, and the masked ball sprite -- passed
- `git diff --check` -- passed
2026-08-22 16:58:32 +02:00
ddidderr 7da0f4d171 fix(game): launch immediately and enforce tilt
Starting from the attract screen previously created an idle ball and required a
second Down press to launch it. Launch the first ball as part of the start action
while retaining the normal launch action for later balls.

Replace the decaying visual-only tilt value with a latched game state. A tilt now
forfeits the current bonus, suppresses scoring and further nudges, returns both
flippers to rest, and remains active until the ball drains. Keep the warning on
the playfield and use the recovered tilt sound instead of treating the state as
a temporary decorative overlay.

Test Plan:
- `cargo fmt -- --check` -- passed
- `cargo test --all-targets` -- passed, including tilt latch, scoring, flipper,
  drain-reset, and launch coverage
- `cargo clippy --all-targets -- -D warnings` -- passed
- `git diff --check` -- passed
2026-08-22 16:54:12 +02:00