Commit Graph

29 Commits

Author SHA1 Message Date
gamer147
d6d1be984e Implement ADV foreground transition lifecycle 2026-07-10 18:49:53 -04:00
gamer147
768863b48e feat: add affine rotation rendering and timeline diagnostics 2026-07-10 17:54:33 -04:00
gamer147
4d225618f9 fix: align animation pacing and transforms with native 2026-07-10 15:41:17 -04:00
gamer147
5427e9d776 feat: split native scale and translation channels 2026-07-10 10:28:11 -04:00
gamer147
f2797d5ab7 feat: model ADV coroutines and retained effect teardown 2026-07-10 09:45:43 -04:00
gamer147
f3f1314848 feat(godot): tint-strength lerp blend + --gfx-log compositor/op diagnostic
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>
2026-07-08 23:41:03 -04:00
gamer147
81f991129a feat(godot): drive the anim interpolator off FrameClock (paced spritesheet+glow)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 22:09:02 -04:00
gamer147
bea5a99139 feat(godot): surfaceless color-fill quads for AE* fades (opening no longer opaque-grey)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:09:18 -04:00
gamer147
560be51011 feat(godot): colorkey bake + per-object alpha/tint blit
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 19:07:48 -04:00
gamer147
9efebc7489 feat: throttle Godot VM to a per-frame op budget on FrameClock (fixes opening speed-through)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 17:32:29 -04:00
gamer147
5366fe2171 docs+godot: finish frame-cadence RE notes + headless shot null-guard
Prior-session WIP: RE findings on the engine frame cadence (engine-re.md,
phase-a-slice-plan.md, tools-reference.md) and a null-guard so headless
--shot-sequence advances without a rendered viewport texture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 17:26:27 -04:00
gamer147
a256b8e6d8 feat(diag): aggregating + filtered trace sinks (histogram, op-filter, per-script)
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>
2026-07-08 09:50:07 -04:00
gamer147
34a1b65670 feat(godot): --shot-sequence per-frame capture for verifying paced animation
Dumps one PNG per _Process frame (auto-advancing past input waits), so a
time-based sleep-paced effect can be verified as distinct frames. Confirmed
the SC0000 opening now steps through paced AE*/character/CG frames instead of
jumping straight to the final state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 09:07:22 -04:00
gamer147
0ed6ff130d feat(godot): Sleep blocks the VM thread so the compositor presents paced frames
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>
2026-07-08 09:02:01 -04:00
gamer147
24f585ebfd feat(gfx): Godot per-object alpha tween + alpha-aware blit + --shot-settle
Retained-object animation channel wired into the compositor: wall-clock tween of
the anim channel over the GLOBAL clock (0x238), opacity from the 3rd vec component,
applied via an alpha-aware BlitLayer. Non-regressing (SC0000 opening page 2 pixel-
identical at settle 3 and 300; selftest OK). Added --shot-settle <frames> to capture
mid-tween.

NOTE: this drives RETAINED-object animations; the opening AE* explosion (AE001D/
AE002B/AE003B) is an immediate-mode slot-0 frame sequence paced by sleep/the render
loop, which our instant execution burst collapses -> needs frame-pacing (scene-
coroutine backlog), not this per-object channel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:48:23 -04:00
gamer147
eeaeabd5cc feat(gfx): Godot per-frame compositor on the surface/object model + restore --boot
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>
2026-07-07 20:38:07 -04:00
gamer147
4aa33378d6 chore(godot): track GodotTraceSink.cs.uid (Godot import artifact)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:27:51 -04:00
gamer147
2b078e1eb8 refactor(godot): report subroutines via GodotTraceSink, not the IHost queue
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:23:25 -04:00
gamer147
5e5968f43b Add subroutine-execution diagnostics + Godot --scene
- 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>
2026-07-07 14:10:55 -04:00
gamer147
6a0e4df7fd Wire provider into Godot (play + selftest); honest full-handling selftest
- Play path: VM now gets Sys4ScriptProvider, so call-script executes live on screen
  (subroutines run; input-wait loops break on real player input).
- Selftest: was 'run SC0000 stubbed, match vm0-trace (186)'. Now runs a SYNTHESIZED
  scene (show-text + wait-for-input + real nested call-script) through the Godot
  thread/semaphore/CallDeferred plumbing and asserts it matches a headless run of the
  same scene — full handling, expected computed live, no frozen golden, no vm0 dep.
- Verified: godot --headless -- --selftest => 'threaded host matches headless (3 lines)'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 13:48:47 -04:00
gamer147
1ab570c854 feat(godot): --seed 0xADDR=VAL for the playable frontend + doc CLI/frontend commands
godot -- --seed 0xa57=1 seeds initial global state in the ADV frontend (e.g.
Lily's form-gated voiced dialogue plays live; forms A/B/C = 0xa57/0xa58/0xa59).
Additive: no seed = unchanged, selftest OK. Also documents the engine CLI
(run/trace/audio/gfx/play/sweep) + Godot frontend flags in tools-reference.md
(they lived only in memory/commits).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 07:54:22 -04:00
gamer147
4eae9515fd feat(a2b): screen-backbuffer blit compositor + real GetTextureSize
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>
2026-07-06 23:02:58 -04:00
gamer147
3512045916 feat(a2b): implement 0x208 get-texture-size (geometry keystone)
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>
2026-07-06 22:48:10 -04:00
gamer147
f18f3a20e9 feat(a2b): audio — wire play-bgm/play-voice, fix BGM addressing
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>
2026-07-06 22:26:54 -04:00
gamer147
f0d75fa0dc feat(a2b): first-pass texture render — full-screen event-CG layer
Wire asset resolution into a live Godot render. VM executes set-texture ->
ResourceMap resolves (scene,resId) -> files[section_base+resId] across all
archives -> pre-converted BMP -> composite behind the dialogue. The full-screen
event-CG layer (EV052*) renders end-to-end from the executed bytecode.

- Age.Engine/Sys4/ResourceMap.cs: Resolve + BMP TexturePath; Paths: asset JSONs
- GodotAdvHost: create/set/draw-texture -> TextureRect in a _stage layer
- IHost.DrawTexture + VM dispatch extended with dst x/y (draw-texture args 7/8)
- project.godot 800x600; convert_agf.py all-archive + --scene batch
- engine 8/8, C# --selftest still byte-matches vm0 trace (VM behaviour unchanged)

Known limitations (next chunk = graphics geometry/blend subsystem):
- sprites + BG* via the CG-load subroutine get garbage dst/size — native ops
  stubbed (0x208 get-texture-size + sprite position/anim chain)
- AE* fades draw opaque/instant (no alpha); no chromakey
- slot model approximates the game's immediate-mode blit-onto-slot-0 canvas
- AGF pre-converted to BMP offline (runtime decoder deferred)
See docs/phase-a-slice-plan.md (A2b section).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 21:22:42 -04:00
gamer147
8b864db36e fix(a2a): advance on mouse click (use _Input; root Control ate clicks in _UnhandledInput)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 15:09:23 -04:00
gamer147
894873da50 feat(a2a): CJK font for the message window; mark A2a done in the slice plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:52:40 -04:00
gamer147
6eb9b4abfc feat(a2a): worker-thread ADV host + blocking wait-for-input + headless self-test
SELFTEST OK: 186 lines match vm0 trace; real SemaphoreSlim gate + CallDeferred exercised headless.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:51:07 -04:00
gamer147
412965c9ca feat(a2a): Godot 4.7 .NET project scaffold referencing Age.Engine (headless smoke)
SMOKE: Paths resolves to age-reimpl inside Godot; Age.Engine parses SC0000 in-process.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:49:57 -04:00