libs/tts was never tracked despite being a build dependency of the gamemaker example. tools/download_tts.sh fetches the public upstream weights (HuggingFace Kokoro-82M + whisper.cpp) and converts them locally with the in-repo stdlib-only converter; model artifacts are gitignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
aigame port findings — my-game 3D sandbox → game.splash
Result of test-porting the AI-generated Godot game (~/games/my-game, the active
3D sandbox: ~2100 lines across main3d + 9 actor scripts) onto the gamemaker
engine as one game.splash. Fixture:
examples/gamemaker/resources/fixtures/sandbox3d.splash (~370 lines — 5.7×
denser than the GDScript, mostly because the engine owns spawning/physics/SFX).
Written 2026-07-09. Companion checklist: aigame_port_inventory.md.
Verification
- Whole world evaluates clean first-load: 1036 entities (961 terrain columns, water sheets, 14 trees, goal + beacon, 19-creature cast, 2 trucks), zero eval or tick errors at the 500k/tick instruction limit.
- Tape run (
ag test, 200 frames): spawn→fall→land at exactly ground+half; camera-relative walk at exactly SPEED 6.0; jump arc peaks +2.25 = JUMP²/2G to the decimal; wander AI steers the cast; respawn works. Probe numbers, not hopes. - Mount/drive/dismount verified end-to-end by accident: the first fixture spawn was 2.3 units from a truck and the player silently auto-mounted at tick 1 — seat height, top speed, and jump-to-dismount all matched the Godot behavior before I even tried to test them. (Fixed by matching the original's player transform (-8, 11, 8).)
- NOT verified visually (headless pane compositing still pending, task #9): colors, camera feel, HUD text rendering. Not exercised behaviorally: nightmare wake/pounce and the win touch (code-verified only; both use the same verbs the probes exercised).
Engine bugs the port flushed out (both fixed)
- Sensors were solid.
step_worldfiltered collision candidates by body kind only —sensor: trueboxes (goals, my water sheets, the beacon) blocked movement, contradicting the documented contract. One-line filter fix. The starter game never noticed because its goal floats at jump apex. - (Earlier, same session) optional positional args trapped — probing
args[1]on a 1-arg call failed the whole eval. Every optional arg would have hit this.
A port whose only failures were engine bugs, not script bugs, is a good sign for the DSL's authorability.
The six predicted cleanups — verdict from actually porting
| # | Prediction (aigame_port_inventory.md) | Verdict |
|---|---|---|
| 1 | First-class SFX bank + synth | Confirmed hard. The corpus had invented squeak and roar beyond the bank we shipped — the bank grew twice in one day. Named-bank + beep/jingle is the right shape; expect the bank to keep growing from corpus usage. |
| 2 | Tags + broadcast/duck-dispatch | Half-confirmed. find/tag/distance + script-side state objects covered everything the port needed — cross-actor "messages" (enrage/zap/heal) became plain field writes on shared script objects, which is better than Godot's group+has_method bus. A native broadcast API is NOT needed while one script owns all actors. It becomes needed only if games ever split across isolates. Deferred, deliberately. |
| 3 | Script raycast | Dodged, honestly. Ledge-probe AI was replaced by heightmap lookup (h_at) because terrain heights are script data anyway. Fine for this game; a shooter that needs line-of-sight will force game.raycast. Keep on the roadmap, don't build speculatively. |
| 4 | Camera-relative helper | Confirmed, split in two. The missing primitive wasn't a movement helper — it was game.cam_yaw() (the orbit camera's yaw was engine-private). With yaw readable, two lines of cos/sin in script do the rest; documented as a pattern instead of an API. |
| 5 | attach/detach (seats/carrying) |
Confirmed emphatically. Replaced ~100 lines of per-actor teleport-following + collision toggling in GDScript with 2 calls. Vehicles, passengers, and the dismount-pop all fell out. |
| 6 | Mover platform carry | Already in the engine (kinematic floor_id carry); the port didn't stress it (no moving platforms in the 3D sandbox — they're in the 2D game). |
New findings (not predicted)
- No RNG in the script language — the single most-used Godot facility
(randf/randi everywhere in wander AI) simply didn't exist. Added
game.rand()/game.rand_range(a,b), xorshift seeded per eval: wander AI now replays identically under input tapes, which Godot'srandomize()corpus could never do. Determinism became a feature of the port. - No noise either — the terrain wants value noise. A 12-line script
hash/smooth/noise2worked fine at 31×31. At the original's 256×256 it wouldn't (65k columns ≈ 3M+ instructions, and 65k entities would swamp both the O(statics×movers) physics and the instanced renderer). The Godot game itself had to weld chunks — scale is an engine concern in any engine. → Roadmap:game.terrain({cells, cell, heights})native heightfield (box3d has heightfield shapes waiting) + greedy column merging. shootwas a hardcoded gap — the ActionMap knew jump but nothing else; the corpus registers custom actions at runtime (mouse+F+gamepad X). Added F →shoot/shoot_pressed. Real fix on the roadmap:game.action("dash", "KeyQ")runtime action registration, like the corpus does in Godot.- Projectiles work but are clunky: a bolt = gravity-0 mover + set_vel +
script-side life/hit bookkeeping in a
retainclosure. Fine at 5 bolts; a bullet-hell would wantgame.spawn_projectile({vel, life, on_hit})with engine-side lifetime. Mover-vs-mover hits also aren't reported byon_touch(sensor×mover only) — the port used distance checks; real overlap events for movers is a small, worthwhile addition. - One HUD line is enough — the original's 5 labels + colors + cancel-token
flashes collapsed into
game.text+ a 6-line scriptflash()with a generation counter. Colored/positioned HUD text can wait. - Multi-part models are the visible fidelity loss. Every creature is one
colored box; the originals have legs/shirt/head/eyes/smile built from 6–12
cubes, billboard nametags, emissive eyes. The single biggest visual upgrade
per line of API:
game.part(id, {offset, size, color})decorative child boxes (no physics), plusgame.label(id, text)nametags. This is also what made the Godot corpus charming — worth prioritizing over new mechanics. - Script ergonomics held up. Struct-arrays of actor state + one shared
steering function expressed 9 GDScript classes in ~120 lines. Field mutation
through array elements, closures capturing top-level
lets, andretainwith side effects all just worked. The0.0 - xworkaround for (possibly fine) unary minus should be tested and, if broken, fixed — it's the ugliest thing in the fixture.
Fidelity ledger (what the port drops vs the original)
- Terrain at 1/8 resolution (31×31×4u columns vs 256×256×0.625u welded chunks): terraces are chunkier, no caves (cave-carving needs the fine grid to read as tunnels), no smooth triangulated slopes, no vertex-color blending.
- Retired cast stays retired (Huggy, Robot, soldiers — the original has them commented out too), so Kissy's enrage/defend/flee arcs are dormant here as there; her follow behavior is live.
- Single-box creatures, no nametags, no arm-reach animation, no emissive glow, no procedural sky (fixed background), water is an opaque thin sheet (alpha untested in the cube renderer), no camera-blocking ray (camera can clip through hills), no mouse-capture (orbit-drag instead), mini-variants are recolored critters.
- Behavior approximations: injured-Kissy heals into a follower (original swaps to a full Kissy with her own arc); nightmare glow-eyes become a body-color swap; critter squeak probabilities eyeballed.
Bottom line
The port took one authoring pass, found two engine bugs and five API gaps, and every gap closed with a small primitive rather than a framework. The engine's current vocabulary + script-side state objects genuinely cover the corpus's behavioral range; the visible gap is decoration (parts/labels), and the structural ceiling is terrain scale — both have clear, small next steps.
v2 re-port (2026-07-09 late) — the full current game
fixtures/sandbox3d.splash v2 ports the game's CURRENT state (~3600 GDScript lines,
44 creatures + 2 trucks) onto the upgraded engine: verified headless — eval clean at
90 entities, tape run writes test_done/captures/probes, plaza floor exactly 7.9 and
walk exactly 6.0 (Godot ground truth), and two identical runs produce byte-identical
probes. The tape also exercises the full truck mount/drive/dismount chain.
Fidelity ledger
Exact: every spawn-table name/position/size/color/speed; all behavior constants — Giant DogDay guardian (follow 34/stand 9.5, charge 8.4 to an intercept point, bonk 5 → stagger+knockback+bark, golden beam flash), Kissy bodyguard (gates 18/34, intercept, shove 2.2), headcrabs (chase 26, leap at 6 [up 8 fwd 7, cd 1.8], latch 1.5 → ride-attach (0,1.95,0) spin 2 + speed_mult 0.5, shake-off on vel.y>5 or 4s, self-stun 2.5), Prototype weeping-angel (LOS dot 0.55 in 34, creep 70, eye glow 1.5/4.5), Baba Chops (charge 14 @7.2, ram 2.4, fire-eye glow 2→5 ramp), Nightmare Huggy (hunt 90, arm-reach via move_part inside 30), nightmare critters (creep 30, nip 1.3 bounce), CatNap (sleep-curl scale 1/0.6/1, wake 9, catch 1.9), farm animals (shy 4.5/flee 4.4, calls 4–14s at Godot's per-kind pitches), trucks/passengers, terraced terrain recipe (12.5+noise, snap 0.5, plaza 26/14/7, water 3.5, height-color bands), goal on the true peak + glowing beacon, third-person camera (1.6/10/−0.35) + crosshair + hint, zap dispatch on all threat kinds, HUD flash tokens, win latch, full synth bank.
Approximated: terrain colors are single-color auto-shade (Godot has height bands sand/grass/dirt/stone/snow — a 257² script colors array would blow the eval budget; engine follow-up: band colors as terrain options); models simplified but silhouette-faithful (DogDay 21 parts incl. the 8-box sunbeam collar vs 23; Baba 15 vs 27 — horns straight not curled); animal voices = moo/squeak at Godot's pitch tables (no dedicated oink/baa/cluck recipes); only Nightmare Huggy's arms animate (pack arms static); zap stun uniform 4.5s (Godot 4–5.5 per kind).
Dropped: cave tunnels only. (x-major→z-major heightfield transpose handled;
goal verified on the true peak via game.ground_peak().)
Engine gaps: found by the port, then closed engine-side (same day)
- CLOSED — eval budget vs terrain scale.
game.terrainnow runs its noise engine-side (freq/offset/step/min/max/plaza, cells up to 384), plusgame.ground_y(x, z)andgame.ground_peak(). The fixture builds the full Godot-scale 257×257 world (256 cells @0.625u) with zero script instruction cost, and all spawn/tree/goal heights come from ground queries. - CLOSED — second input action.
grabexists (keyboard G, gamepad B, tape action) withgrab/grab_pressedsnapshot fields. The grapple hand is ported: 32 u/s flight to range 16 on agame.beamcable, terrain hit → player yank (22 u/s + 6.5 up), creature hit → ride-attach haul, held 1.2s, set down at the player's feet with a pop. Approximations (memo): solid-hit detection is terrain-height only (tree/box bodies don't catch the cable — needs a script raycast/box-probe verb); the hand vanishes at max range instead of retracting. - CLOSED — label outlines. Engine draws a 4-copy dark outline behind every billboard label; nothing needed in script.
- OPEN — height-carving. Caves need holes/overhangs in the heightfield (or rock-slab CSG); the smooth mesh is single-valued.
- OPEN (new, minor) — terrain band colors. Height-band coloring (sand/grass/dirt/stone/snow) as engine-side terrain options, since script-built color arrays don't scale past ~100² vertices.
- OPEN (new, minor) — solid-probe verb. A
game.raycast/box-probe would let the grapple (and ledge-AI patterns from the corpus) hit boxes, not just terrain.
Verification after the engine-noise/grapple update: eval clean at 90 entities, zero JIT failures, last_error empty, tape (walk/jump/shoot/grab) → test_done + captures + probes with plaza floor exactly 7.9 and walk exactly 6.0, two runs byte-identical.