Commit Graph
33 Commits
Author SHA1 Message Date
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
ddidderr e19162d54d fix(game): reopen the drain and recover stalled balls
The recovered bottom apron had been represented as one solid collision segment,
which prevented the ball from reaching the drain threshold. Split that apron
around the visible center drain so a lost ball decrements the ball count and
allows player and game-over progression to continue.

Add a conservative ball-search impulse after three seconds below the movement
threshold. This protects long-running games from other low-energy rests without
changing active trajectories, and reuses the original nudge sound as feedback.

Test Plan:
- `cargo fmt -- --check` -- passed
- `cargo test --all-targets` -- passed, including drain and ball-search regressions
- `cargo clippy --all-targets -- -D warnings` -- passed
- `git diff --check` -- passed
2026-08-22 16:52:42 +02:00
ddidderr fe33ef902f fix(assets): mark legacy data files non-executable
The source copy preserved executable permission bits from the evidence archive
on HISCORES.DAT and TDKPIN.INI. Keep their exact bytes while recording them as
ordinary embedded data files in the reconstruction repository.

Test Plan:
- `git diff --cached --check` -- passed
- verified the staged changes are mode-only transitions from 100755 to 100644
2026-08-22 16:12:34 +02:00
ddidderr f817b63683 feat: rebuild TDK Pinball Machine in Rust
Replace the empty Rust scaffold with a playable native reconstruction of the
1995 Win16 game. Embed the complete decoded asset set, retain the original
640x460 presentation and localized help, and implement a resizable letterboxed
Macroquad frontend for Linux, macOS, and Windows.

Recreate the multiplayer ball flow, flippers, nudging, target banks, wheel,
robot, lock, magnets, bumper progression, TDK diamond multiplier, media extra
balls, sound dispatch, settings, and high-score entry. The physics engine uses
a fixed time step and 101 static collision segments transcribed from the
original 175-object table. Portable JSON persistence imports and decodes the
original XOR-obfuscated high-score file on first run.

Document the evidence boundary explicitly: artwork and PCM samples are exact,
static geometry is recovered, and several numeric scoring/impulse values remain
best-evidence behavioral tuning rather than bit-identical Win16 arithmetic.
Add a native three-OS CI matrix and retain every decoded resource for future
fidelity work.

Test Plan:
- `diff -qr original/assets/decoded tdkpin-rs/assets/original` with the two
  intentionally added legacy root files excluded -- passed
- `cargo fmt --all -- --check` -- passed
- `cargo check --all-targets` -- passed
- `cargo test --all-targets` -- passed, 8 tests
- `cargo clippy --all-targets -- -D warnings` -- passed
- `cargo build --profile production` -- passed
- `cargo check --all-targets --target x86_64-pc-windows-gnu` -- passed
- `cargo check --all-targets --target x86_64-apple-darwin` -- passed
- `cargo run` graphical start, launch, collision, and score smoke test -- passed;
  audible output was unavailable because the host has no ALSA device
2026-08-22 16:12:11 +02:00
ddidderr 54061f1eb7 init 2026-08-22 15:52:52 +02:00