Compositor applies TintStrength as a texel->tint LERP (0=keep texel) with object
opacity independent. New --gfx-log <file>: per-frame per-object draw/skip CHANGE
log + set-texture/create-texture slot trace — the tool that root-caused the grey
background (everything collapsing into slot 0).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The existing trace framework only had a flat text formatter, so every question
became 'dump millions of lines, then grep'. This session that cost a long wrong
detour. Add, all observe-only (parity preserved):
- HistogramTraceSink: execution counts per opcode AND per call-site (script:pc)
with a sample operand. Dumped sorted after the run. This is what instantly
showed the 493k headless 'sleep's are INPUTNAME.BIN:0x1c3 (a name-entry poll
loop), not the opening.
- TraceSinkBase: tracks the frame stack -> attributes each step to its REAL
script (nested call-script frames included) = the 'which script is this pc in?'
answer a bare step trace can't give.
- TextTraceSink: op-filter (--trace-ops sleep,draw-texture,...) + script:pc tags.
- CompositeTraceSink: fan-out (text + histogram + Godot's call-script queue).
- OpcodeTable.ByLabel: mnemonic -> opcode for --trace-ops.
- CLI: --trace-histogram, --trace-ops, robust --trace-file (mkdir -p).
- Godot: --trace-histogram <file> profiles the REAL run (headless flow diverges:
real run to page 1 is 562 steps / 0 sleeps vs headless 2M steps / 493k sleeps).
- Also: --sleep-scale <f> debug knob to slow the paced opening for inspection.
Engine 56/56 (4 new); sweep parity 284/13; Godot builds + dogfooded end-to-end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
op 0xc8 -> Thread.Sleep(duration ms) on the VM background thread (capped 10s);
main thread keeps compositing -> the sleep-paced opening burst gets frames to show.
Unit = ms (RE-confirmed). Selftest OK (plumbing parity; synthetic scene has no sleep).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Main._Process composites visible objects in ascending-handle order from their live
surface (resId->BMP); GodotAdvHost.DrawTexture no-op'd (retained), ResolveResIdTexture
added. Restored the --boot system-boot handling that the revert had dropped (Main.cs
was running cold, which is why nothing matched the oracle). Verified: booted opening
event CG renders correctly. Selftest green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CLI run: report call-script dispatch count + distinct source scripts per run.
- Godot --scene <NAME>: play any scene (not just SC0000).
- Godot reports the call-scripts executed as nested frames at scene end (collected
thread-safely; Godot drops GD.Print from the VM background thread).
Demonstrated live: SC0240 in Godot executes 29 call-scripts (RESETLAND, SETEN,
ADDEN, RENDERMAP, SETOBJ, DRAWOBJ, CALCREVISE, LOOK) as nested subroutine frames.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Godot host: source images kept per slot with dims read from the BMP header on
the VM thread (synchronous, so the bytecode's geometry math sees real sizes);
draw-texture blits src rect -> dst into one 800x600 canvas in execution order,
shown by a single TextureRect. Selftest byte-parity preserved.
Slot 0 = primary/screen surface: seed its dims to 800x600 (the boot-time
create-texture the single-scene harness skips) and record create-texture(w,h),
so the first CG's anchor-preserve math stays an identity instead of corrupting
the persistent base globals. Fixes the grey-first-CG opening. Verified: SC0000
pages 1/3 composite the opening event CG at (0,0) with dialogue over it.
Also adds a --shot <png> [--shot-page N] dev capture to Main (page-gated on a
VM-thread counter) for headless visual verification.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Promote 0x208 from stub to a real VM op that writes the loaded texture's
width/height into its two output globals, via new IHost.GetTextureSize. This is
the single native primitive the CG-load subroutine (SC0000 label_12649) needs;
all downstream centering/anchor geometry is already computed in bytecode.
Non-Godot hosts return (0,0) so trace/selftest parity holds (engine 9/9 incl.
TraceDiffTests). Godot host gets a temporary (0,0) stub; real impl in the
compositor task.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire audio end-to-end (OGG plays natively in Godot; no Frida, no decode):
- IHost.PlayBgm/PlayVoice + VM dispatch for play-bgm(0xbf)/play-voice(0xc4).
Non-Godot hosts no-op them so --selftest + engine 8/8 stay byte-identical.
- GodotAdvHost resolves id->OGG; Main plays via two AudioStreamPlayer nodes
(BGM looping; voice interrupt-on-new).
Key finding (by-ear, systematic-debugging): the two audio ops use DIFFERENT
addressing — the earlier "unified manifest" assumption was wrong for BGM.
- play-voice -> per-scene manifest files[base+id] (offset 0, same as textures).
Confirmed by ear; upgraded med->HIGH.
- play-bgm -> DIRECT LITERAL NAME BGM{id:03d}.OGG, NOT the manifest.
Real game: play-bgm 5->BGM005, 8->BGM008 (manifest gave +1). Proven by
play-bgm 0x23->BGM035 (real standalone track; BGM set skips 030-034) that the
manifest mis-resolved to a graphics entry. Fix is BGM-only:
ResourceMap.BgmPathById; voices/textures unchanged.
Also: Lily's silence root-caused as correct form-gating (G[0xa57/0xa58/0xa59]),
left unseeded by choice (no dummy state). Added diagnostic
`Age.Cli audio <SCENE.BIN> [0xADDR=VAL ...]`. Corrected opcodes.toml (play-bgm
direct-name, play-voice HIGH) + docs/memory (dropped the bogus unified-manifest
/ Frida-BGM006 claims).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>