Commit Graph

6 Commits

Author SHA1 Message Date
m3ultra
7347ec4db7 Pinball: Rules, Juice, HUD, a table probe — and the three bugs it found
Four lanes built in parallel onto the data-driven spine:

- Rules.gd (509) — score with a decaying combo multiplier, 3 balls, ball save,
  escalating drop-bank bonuses, lit rollover lanes that level up when swept,
  saucer-lock multiball, two-warning tilt, end-of-ball bonus, per-table high
  scores in user://pinball_scores.cfg.
- Juice.gd (784) — 12 procedural voices synthesized at boot, no assets. The
  spinner ratchet is timed to Table.gd bleeding spin_left at 6.0/s so the ticking
  stops exactly when the blade does. Pooled voices/lights/sparks, quadratic-falloff
  shake, and a damped spring for nudge. Palette-tinted lights with a value floor,
  because a light the colour of a near-black saucer illuminates nothing.
- Hud.gd (935) — score that ROLLS UP rather than snapping, combo, award callouts,
  plunger meter, tilt banner, ball save, game over panel.
- dev/probe_tables.gd (480) — the gate: builds every table and checks parts, part
  overlaps, playfield bounds, flipper swing, that the ball actually DRAINS, and
  that everything settles.

The probe immediately found three real bugs the flipper autotest could not see:

1. The drain sat 3 cm PAST the playfield lip, so on four of five tables the ball
   rolled off the end into the void and the trigger never fired. A table you cannot
   lose a ball on is not a table. Moved inside the slab.
2. Inlane posts were level with the slingshots and interpenetrated them on three
   tables — on a real table the posts sit below. Moved down-table.
3. THE DIG's counter ramp ran through the crate bank, and a 15 deg yaw over a 28 cm
   ramp swings its far end 7 cm sideways, which then walked it through the left rail.
   Less yaw, tucked in, crates slid right.

PROBE_TABLES: PASS 5/5. AUTOTEST: ALL TABLES OK.

Still open, and it is an ENGINE finding not a table one: godot-box3d does not enforce
HingeJoint3D angular limits — flippers swing 147-160 deg against a 62 deg limit. The
probe reports it as a NOTE per table. Worth an upstream issue alongside the motor-sign
inversion already in the README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 18:18:15 +10:00
m3ultra
5adf299271 Pinball: two more tables — ORBITAL and THE DIG
ORBITAL: everything funnels to the centre. A long central ramp with orbit lanes and
a spinner either side, twin saucers as the multiball locks, and a four-pop diamond
at the top so a ball that gets up there rattles.

THE DIG: the record shop table, and the one that belongs to this house. The bank is
a six-crate row you flip through one sleeve at a time (clearing it is 'finding the
record'), the ramp is the counter, the spinner is a turntable, the saucer is the
listening booth, and there's a wall of rare sleeves to pick off. Same universe as
the shop level in Destroyulator; only the theme is borrowed.

Five tables now, all passing the autotest: 24/21/28/23/25 parts, 11 flippers total,
67-86 deg swing under Box3D.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:28:04 +10:00
m3ultra
2f1a1250cc Pinball: data-driven table spine (3 tables, 11 part kinds)
Replaces the single hand-built .tscn table with the Floorplan/Levels pattern from
Destroyulator: Table.gd builds a playfield from a spec, Tables.gd holds the specs,
Main.gd orchestrates. A table is now a Dictionary you can diff and hot-swap (T),
parallel work doesn't collide in one scene file, and a headless autotest builds
every table and proves the flippers swing.

Part vocabulary: flipper, bumper, sling, target, drop (banked, with cleared
detection), spinner (scores per revolution), saucer (captures + ejects), rollover
(lit lanes/glyphs), ramp (speed-gated, scores only at the top), wall, post.

Three ORIGINAL tables — NEON ARCADE (wide open, 3-bank, spinner lane), THE GROTTO
(narrow, open outlanes, a 5-bank guarding the only ramp), THE FOUNDRY (long, twin
ramps, upper flipper, 2x3 target array). Not traced from the vendored decomp.

Main also centralises the engine divergence: Box3D's hinge motor runs opposite to
Jolt/GodotPhysics, so flip_sign is resolved once at boot from the engine name
rather than authoring two sets of tables.

Caught by the new autotest: Godot's HingeJoint3D spins about its own local Z, so a
flipper built in code needs the joint stood on end to swing about world Y. Commanded
correctly and utterly motionless until fixed. AUTOTEST now: 3 tables, 7 flippers,
68-84 deg swing, ALL TABLES OK.

Rules/Juice/Hud are spawned by class name if present, so those lanes land independently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 17:24:07 +10:00
m3ultra
5fa6bbf67b Add Box3D upstream research notes; note godot-box3d is single-threaded
Engine notes from the eval session (Apple Silicon 24-P-core worker cap, the
benchmark harness, binding gaps, provenance), linked from the README.
Annotated with a verified fact: the binding hardcodes workerCount=1, so the
E-core trap cannot trigger through Godot until it grows a task system.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 09:03:34 +10:00
m3ultra
804e4c0fb8 Add Box3D upstream research notes for the testbed
Findings from the engine evaluation that led to this project, kept separate from
the table's own A/B results:

- Apple Silicon threading: Box3D gets SLOWER past the performance-core count on
  the M3 Ultra (30 workers 5158ms -> 32 workers 5646ms, 24P+8E). A physics step
  is a barrier, so a share of the work on a third-speed E-core makes the whole
  step wait. Read hw.perflevel0.physicalcpu, never hw.ncpu. Applies to any
  threaded physics on this fleet, not just Box3D.
- The benchmark harness: the CMake option is BOX3D_BENCHMARKS (plural), the
  scene/flag list, upstream's M2 Air + AMD baselines, and a warning that a full
  sweep runs over an hour on 32 cores — always pin -b and -w.
- What the binding does NOT implement (Generic6DOF, ConeTwist, SoftBody3D,
  per-pair exceptions) plus the silent Area3D-vs-trimesh divergence.
- Why pinball and not the ragdoll game: ~/Documents/alright is the better fit but
  is BLOCKED — its ragdoll is built from Generic6DOFJoint3D. Watch that upstream.
- Provenance: Box3D is serious (Catto, MIT, s&box + Esoterica adoption); the
  binding is five weeks old with zero releases and self-declares experimental.
  This is a testbed, not a bet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 08:54:25 +10:00
m3ultra
2ca67bb28b Add Godot 4.7 physics testbed: Box3D/Jolt/GodotPhysics A/B pinball table
Playable table (hinge-motor flippers, plunger, bumper, drain) plus a headless
probe harness that measured, per engine: tunneling of a fast ball through a
1 cm wall with CCD off/on, and hinge-motor drive. Findings in godot/README.md:
Box3D never tunnels even with CCD off, but its hinge motor sign is inverted
vs Jolt/GodotPhysics, and Jolt blows through angular limits that Box3D holds.
Ships a from-source macOS arm64 libgodot-box3d.dylib (upstream has no
releases). Also gitignore the remaining Microsoft media at repo root.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 05:33:07 +10:00