scenes/maps/playground.tscn and crag.tscn are generated by the map scripts' Rebuild (inspector
button, or godot-mono --headless --path . tools/rebuild_maps.tscn) and loaded as saved by the game,
so hand edits stick. The crag route is saved as Marker3D nodes and crag_spike checks the saved
scene. Layout verified identical to the code-built one (tools/map_dump.tscn).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
North of the playground, facing spawn: bouldering blocks, a ladder to an 8 m rest ledge,
a jug wall, a 0.4 m traverse ledge, an overhang with monkey bars and a lip bar onto a
16 m rest ledge, a chimney to the 26 m summit with throwable props. A 'Crag' teleport
marker at the base is the way down; teleports now land the floor on the marker's height.
Floor widened to 80 m. Regression: scenes/crag_spike.tscn (reach, free space, overlaps,
height, line of sight).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WorldPanel is now the project's one SubViewport screen: MainMenu (tabs Player /
World / System; Return to spawn, Recalibrate body, Reset props, Ping, click
volume slider, scrollable log, FPS, Close) rendered by MenuScreen into a
960x640 SubViewport on a thin 0.36x0.24 m panel.
Input = touch-first hybrid cursor injected as mouse events: both controller
rays are projected analytically onto the panel plane, converted to viewport
pixels and pushed via Viewport.PushInput (MouseMotion/MouseButton, wheel from
thumbstick). Stylus tip within 6 cm = touch cursor (press crossing 0.6 cm from
the front, release past 1.8 cm); farther = laser cursor with trigger edge. One
owning hand per viewport. Haptic tick + synthesized click on press.
Kept: ax_button summon/dismiss, XROrigin3D parenting, grip drag (direct/laser)
with head re-orient, IsBlocking/IsPanelCollider. Locomotion's beam now takes
its length from WorldPanel.TryGetLaserDistance (no beam while touching) and
starts at the stylus tip. Recalibrate calls Recalibrate() on the first node in
group "calibration" (disabled when absent).
New headless regression scenes/menu_spike.tscn ("MENU: overall PASS").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PhysicsArms: per side 2.0 kg upper arm + 1.2 kg forearm RigidBody3D PD-chasing their
analytic 2-bone IK pose, solved per physics tick from the physical chest shoulders to the
physical hands; no joints (Box3D has no usable shoulder/wrist joint; AGENTS forbids joints
between tracked bodies). Budgets from a 60 kg mass stat: hand 90 < forearm 180 < upper
arm 360 < chest 2400 N. Owns calibration + reach budget (moved from BodyRig); 1 m snap,
non-finite reset, BodyMath.RotationError servo.
- Layer 5 = player arms (mask 1); props/table/floor masks include it; no self-collision.
- BodyRig: visuals only, arms drawn shoulder -> physical elbow -> wrist.
- ArmSpike: track gap 0, swing lag <= 6 cm, arm pushes prop with hand clear, forearm blocked
by bar without tunneling and quiet when pinned, teleport snap, NaN reset — PASS x3.
- CollapseSpike now runs the full body (49/49 x3); all six spikes PASS x3.
- HANDOFF/AGENTS: Stage 2 entry (item 10), layers, tuning table, Stage 2b next steps.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- BodyMath.RotationError: atan2-based shortest-arc error (no NaN, no GetAxis gain collapse
inside 0.16 deg); used by PhysicsTorso orientation servo and PhysicsHands rotation lock
- BodyMath.ResetIfNonFinite escape hatch on torso + hands
- PhysicsHands.Drive extracted (static) so spikes run the real hand chase
- CollapseSpike: deterministic repro (slow yaw drift across a parked chest, 7/40 NaN on old
code) + solver-stacking scenarios (seated chest in table, two-hand-welded 50 kg anvil,
real PropShop) — 49/49 clean after fix, x3; all other spikes PASS x3
- Failure brief RESOLUTION section; HANDOFF/AGENTS updated
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>