From 803df2826f08a761f7c43c7e7c017ec05f990d57 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Sun, 2 Aug 2026 23:54:44 -0400 Subject: [PATCH] Extract VM animation opcode handler --- docs/PROJECT-STRUCTURE.md | 2 + docs/remake-architecture-and-roadmap.md | 13 +++-- .../Age.Engine/Vm/VirtualMachine.Animation.cs | 54 +++++++++++++++++++ engine/Age.Engine/Vm/VirtualMachine.cs | 30 +++-------- 4 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 engine/Age.Engine/Vm/VirtualMachine.Animation.cs diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md index 8c4fe4b..1e3c8e2 100644 --- a/docs/PROJECT-STRUCTURE.md +++ b/docs/PROJECT-STRUCTURE.md @@ -183,6 +183,8 @@ surface release; its labels remain at their existing dispatcher positions around object cases. `engine/Age.Engine/Vm/VirtualMachine.RetainedObjects.cs` owns retained-object registry queries, default-slot and geometry mutation, direct and range transforms, clone, and erase dispatch; animation, surface, ADV-binding, and presentation labels remain in their respective dispatcher groups. +`engine/Age.Engine/Vm/VirtualMachine.Animation.cs` owns retained spritesheet and color channels, timed and cyclic +transforms, per-object animation control, frame-time sampling, and the shared animation-clock opcode handler. The disposable `build/page-map-.jsonl` files are produced by editor/development Godot runs and map runtime ADV page ordinals to their authoritative script offsets for `tools/locate_page.py`. Packaged exports diff --git a/docs/remake-architecture-and-roadmap.md b/docs/remake-architecture-and-roadmap.md index 68cc79c..2dfbcf8 100644 --- a/docs/remake-architecture-and-roadmap.md +++ b/docs/remake-architecture-and-roadmap.md @@ -649,6 +649,12 @@ do not mix mechanical moves with semantic changes. color, surface release, ADV binding, and presentation behavior remain outside this handler. Runtime validation remains green. + The fifth bounded `VirtualMachine.Step` extraction moved retained spritesheet and color channels, timed and + cyclic transforms, per-object animation control, frame-time sampling, and shared animation-clock dispatch into + `engine/Age.Engine/Vm/VirtualMachine.Animation.cs`. The top-level dispatcher retains all labels at their + existing positions and routes the separated groups through `StepAnimation`; presentation transitions and movie + masking remain outside the guarded handler. Runtime validation remains green. + **Gate:** no externally visible behavior or command changes; generated artifacts are byte-identical where deterministic, and the corresponding engine, Python, Godot, and corpus validations remain green after each domain move. @@ -1020,9 +1026,8 @@ layer's rendering diverges from ADV; save layout. ## 8. Immediate next step Continue step 2 of the **codebase consolidation** maintenance slice: behavior-neutral physical splits backed by the tracked launcher and layered validation driver. With the planned `Main`, `GodotAdvHost`, and `GfxState` -domains isolated and the audio, movie, and surface/texture `VirtualMachine.Step` families routed through domain -handlers, with retained-object query/mutation and transform dispatch now isolated as well, extract the retained -animation/color/spritesheet opcode family next without replacing the proven dispatcher or changing public types, -commands, and generated output. +domains isolated and the audio, movie, surface/texture, retained-object, and animation `VirtualMachine.Step` +families routed through domain handlers, extract retained presentation/transition dispatch next without replacing +the proven dispatcher or changing public types, commands, and generated output. Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does not replace Phase B gameplay validation or the open cross-platform gates. diff --git a/engine/Age.Engine/Vm/VirtualMachine.Animation.cs b/engine/Age.Engine/Vm/VirtualMachine.Animation.cs new file mode 100644 index 0000000..b0a83f5 --- /dev/null +++ b/engine/Age.Engine/Vm/VirtualMachine.Animation.cs @@ -0,0 +1,54 @@ +using Age.Engine.Model; + +namespace Age.Engine.Vm; + +public sealed partial class VirtualMachine +{ + private int StepAnimation(string label, IReadOnlyList a, int pc) + { + switch (label) + { + case "u004223C0": // 0x239 spritesheet cell: (handle)(delay)(duration)(frame count)(columns)(cell) + Gfx.SetSrcRect(Read(a[0]), Read(a[3]), Read(a[4]), Read(a[5]), 0); return pc + 1; + case "reset-gfx-cyclic-animations": // 0x230: stop all five retained looping channels + case "u00421E70": + Gfx.ResetCyclicAnimationChannels(Read(a[0])); return pc + 1; + case "u00421EA0": // 0x231 looping spritesheet: (handle)(ms per frame)(frame count)(columns) + Gfx.SetSrcRect(Read(a[0]), Read(a[2]), Read(a[3]), 0, Read(a[1])); return pc + 1; + case "u00421EF0": // 0x232 cyclic packed ARGB; negative alpha/RGB preserve static obj color + Gfx.SetColorAnimResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3])); return pc + 1; + case "set-scale-cycle": // 0x233 (handle)(period ms)(target scale x/y/z percent) + Gfx.SetScaleCycle(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4]))); + return pc + 1; + case "sample-frame-time": // 0x23c: previous <- current; current <- monotonic time + Gfx.SampleFrameTime(_host.InputClockMilliseconds); return pc + 1; + case "gfx-blit-color": // 0x202 (handle)(delay)(duration)(alpha)(color) — one-shot color + Gfx.SetAnimatedObjectColorResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3]), Read(a[4])); + return pc + 1; + case "gfx-draw-color": // 0x203 (handle)(v)(alpha)(color) — static alpha/tint + Gfx.SetStaticObjectColorResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3])); return pc + 1; + // ---- sprite transform / animation cluster (docs/engine-re.md "0x21c-0x243 ... ANIMATION") ---- + case "set-anim-transform-abs": // 0x220 (handle)(delay)(duration)(tx)(ty)(tz) + Gfx.SetTranslationChannel(Read(a[0]), Read(a[1]), Read(a[2]), + (Read(a[3]), Read(a[4]), Read(a[5]))); return pc + 1; + case "set-anim-transform-norm": // 0x21e (handle)(delay)(duration)(sx%)(sy%)(sz%) + Gfx.SetScaleChannel(Read(a[0]), Read(a[1]), Read(a[2]), + (Read(a[3]), Read(a[4]), Read(a[5]))); return pc + 1; + case "set-anim-rotation-axis-angle": // 0x21f (handle)(delay)(duration)(axis x/y/z)(angle deg) + Gfx.SetRotationChannel(Read(a[0]), Read(a[1]), Read(a[2]), + (Read(a[3]), Read(a[4]), Read(a[5])), Read(a[6])); return pc + 1; + case "anim-start": // 0x234 legacy name: (handle)(period)(axis x/y/z), cyclic rotation channel + Gfx.SetRotationCycle(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4]))); return pc + 1; + case "set-anim-clock": // 0x238 (duration) — global, non-blocking (host advances it per-frame) + Gfx.SetAnimClock(Read(a[0])); return pc + 1; + case "set-object-animation-detached": // 0x242 (handle)(flags): bit 0 is nonblocking/force-proof + Gfx.SetOneShotAnimationControl(Read(a[0]), Read(a[1])); return pc + 1; + case "reset-anim-clock": // 0x243: force unprotected one-shots and reset the global service clock + Gfx.ResetAnimClock(); return pc + 1; + case "set-gfx-animation-service-flags": // 0x24e: bit 1 suppresses op 0x243 + Gfx.SetAnimationServiceFlags(Read(a[0])); return pc + 1; + default: + throw new InvalidOperationException($"Non-animation opcode routed to animation handler: {label}"); + } + } +} diff --git a/engine/Age.Engine/Vm/VirtualMachine.cs b/engine/Age.Engine/Vm/VirtualMachine.cs index cc39d7c..9dafeb0 100644 --- a/engine/Age.Engine/Vm/VirtualMachine.cs +++ b/engine/Age.Engine/Vm/VirtualMachine.cs @@ -2550,25 +2550,20 @@ public sealed partial class VirtualMachine case "set-gfx-range-scale-target": // 0x22d (delay)(duration)(sx%)(sy%)(sz%) return StepRetainedObject(label, a, pc); case "u004223C0": // 0x239 spritesheet cell: (handle)(delay)(duration)(frame count)(columns)(cell) - Gfx.SetSrcRect(Read(a[0]), Read(a[3]), Read(a[4]), Read(a[5]), 0); return pc + 1; case "reset-gfx-cyclic-animations": // 0x230: stop all five retained looping channels case "u00421E70": - Gfx.ResetCyclicAnimationChannels(Read(a[0])); return pc + 1; case "u00421EA0": // 0x231 looping spritesheet: (handle)(ms per frame)(frame count)(columns) - Gfx.SetSrcRect(Read(a[0]), Read(a[2]), Read(a[3]), 0, Read(a[1])); return pc + 1; case "u00421EF0": // 0x232 cyclic packed ARGB; negative alpha/RGB preserve static obj color - Gfx.SetColorAnimResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3])); return pc + 1; case "set-scale-cycle": // 0x233 (handle)(period ms)(target scale x/y/z percent) - Gfx.SetScaleCycle(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4]))); - return pc + 1; + return StepAnimation(label, a, pc); case "u00421940": // 0x228: (succ)(handle)(outX)(outY)(outZ) <- target translation matrix return StepRetainedObject(label, a, pc); case "u00422930": case "query-surface-stop-time-ms": case "query-movie-surface-active": return StepMovie(label, ins, pc); - case "sample-frame-time": // 0x23c: previous <- current; current <- monotonic time - Gfx.SampleFrameTime(_host.InputClockMilliseconds); return pc + 1; + case "sample-frame-time": + return StepAnimation(label, a, pc); case "set-gfx-geom3-c": case "u00420620": // upstream ABI label case "gfx-set-scale-current": // 0x1fd (handle)(sx%)(sy%)(sz%) -> current scale matrix @@ -2596,31 +2591,18 @@ public sealed partial class VirtualMachine _host.ReleaseSurface((int)Read(a[0])); Gfx.ClearSurface((int)Read(a[0])); return pc + 1; case "clone-gfx-object": return StepRetainedObject(label, a, pc); - case "gfx-blit-color": // 0x202 (handle)(delay)(duration)(alpha)(color) — one-shot color - Gfx.SetAnimatedObjectColorResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3]), Read(a[4])); - return pc + 1; - case "gfx-draw-color": // 0x203 (handle)(v)(alpha)(color) — static alpha/tint - Gfx.SetStaticObjectColorResolved(Read(a[0]), Read(a[1]), Read(a[2]), Read(a[3])); return pc + 1; + case "gfx-blit-color": + case "gfx-draw-color": // ---- sprite transform / animation cluster (docs/engine-re.md "0x21c-0x243 ... ANIMATION") ---- case "set-anim-transform-abs": // 0x220 (handle)(delay)(duration)(tx)(ty)(tz) - Gfx.SetTranslationChannel(Read(a[0]), Read(a[1]), Read(a[2]), - (Read(a[3]), Read(a[4]), Read(a[5]))); return pc + 1; case "set-anim-transform-norm": // 0x21e (handle)(delay)(duration)(sx%)(sy%)(sz%) - Gfx.SetScaleChannel(Read(a[0]), Read(a[1]), Read(a[2]), - (Read(a[3]), Read(a[4]), Read(a[5]))); return pc + 1; case "set-anim-rotation-axis-angle": // 0x21f (handle)(delay)(duration)(axis x/y/z)(angle deg) - Gfx.SetRotationChannel(Read(a[0]), Read(a[1]), Read(a[2]), - (Read(a[3]), Read(a[4]), Read(a[5])), Read(a[6])); return pc + 1; case "anim-start": // 0x234 legacy name: (handle)(period)(axis x/y/z), cyclic rotation channel - Gfx.SetRotationCycle(Read(a[0]), Read(a[1]), (Read(a[2]), Read(a[3]), Read(a[4]))); return pc + 1; case "set-anim-clock": // 0x238 (duration) — global, non-blocking (host advances it per-frame) - Gfx.SetAnimClock(Read(a[0])); return pc + 1; case "set-object-animation-detached": // 0x242 (handle)(flags): bit 0 is nonblocking/force-proof - Gfx.SetOneShotAnimationControl(Read(a[0]), Read(a[1])); return pc + 1; case "reset-anim-clock": // 0x243: force unprotected one-shots and reset the global service clock - Gfx.ResetAnimClock(); return pc + 1; case "set-gfx-animation-service-flags": // 0x24e: bit 1 suppresses op 0x243 - Gfx.SetAnimationServiceFlags(Read(a[0])); return pc + 1; + return StepAnimation(label, a, pc); case "play-movie-mask-transition": return StepMovie(label, ins, pc); case "queue-surface-alpha-transition": // 0x223: target surface crossfade over two object ranges