native: bring CamDoll FPS system to ac-native so arena.mjs runs unmodified
Pieces that export `system = "fps"` (arena.mjs) now get a live CamDoll
instance attached to api.system.fps.doll on the native runtime — same
class, same source, same API surface as the web build. No piece-side
changes required.
Pipeline:
1. scripts/fps-bundle-entry.mjs re-exports Camera + Dolly from graph.mjs
and CamDoll from cam-doll.mjs. esbuild tree-shakes graph.mjs down
to just the two classes we need (~75 KB total).
2. docker-build.sh + ac-os bundle it as an IIFE (format=iife,
global-name=__FpsSystem) into /jslib/fps-system-bundle.js — mirrors
the kidlisp bundle pattern so it self-executes and exposes the
classes at globalThis.__FpsSystem.{Camera,Dolly,CamDoll}.
3. js_load_piece loads the bundle once at runtime init (beside
kidlisp) and extends the piece export shim: when a piece sets
`system = "fps"`, the shim synchronously constructs a CamDoll from
the piece's fpsOpts, stores it at globalThis.__fpsDoll, and wraps
globalThis.{boot,sim,act,paint} to (a) inject api.system.fps.doll,
(b) drive doll.sim() / doll.act(event) before the piece's own
handler runs.
4. js_call_sim skips the native WASD/trackpad camera driver when a
doll is present (it's authoritative), and — after the piece sim
returns — copies doll.cam.{x,y,z,rotX,rotY,rotZ} into rt->camera3d
so the next frame's graph3d rendering uses the doll's view.
arena.mjs should now be launchable via the native prompt. Same source
as the web piece; if a bug shows up, fix it in arena.mjs or cam-doll.mjs
and both targets inherit the fix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>