Extract VM text history opcode handler
This commit is contained in:
@@ -190,6 +190,8 @@ publication, skip-aware blocking fades/crossfades, foreground-transition waits,
|
|||||||
`engine/Age.Engine/Vm/VirtualMachine.AdvText.cs` owns live ADV text emission, layout/cursor/wait-indicator state,
|
`engine/Age.Engine/Vm/VirtualMachine.AdvText.cs` owns live ADV text emission, layout/cursor/wait-indicator state,
|
||||||
text style and glyph-delay control, direct surface-string and retained numeric-glyph rendering, and retained
|
text style and glyph-delay control, direct surface-string and retained numeric-glyph rendering, and retained
|
||||||
text/wait-object bindings; text history and input/skip/auto services remain separate.
|
text/wait-object bindings; text history and input/skip/auto services remain separate.
|
||||||
|
`engine/Age.Engine/Vm/VirtualMachine.TextHistory.cs` owns history recording control, metadata append/navigation,
|
||||||
|
retained history rendering, metadata/voice lookup, and history backlog clearing.
|
||||||
|
|
||||||
The disposable `build/page-map-<SCENE>.jsonl` files are produced by editor/development Godot runs and map
|
The disposable `build/page-map-<SCENE>.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
|
runtime ADV page ordinals to their authoritative script offsets for `tools/locate_page.py`. Packaged exports
|
||||||
|
|||||||
@@ -668,6 +668,12 @@ do not mix mechanical moves with semantic changes.
|
|||||||
`StepAdvText`; the complete instruction remains available for emission and layout-reset bytecode offsets.
|
`StepAdvText`; the complete instruction remains available for emission and layout-reset bytecode offsets.
|
||||||
Text-history and input/skip/auto services remain outside the handler. Runtime validation remains green.
|
Text-history and input/skip/auto services remain outside the handler. Runtime validation remains green.
|
||||||
|
|
||||||
|
The eighth bounded `VirtualMachine.Step` extraction moved text-history recording control, metadata append and
|
||||||
|
navigation, retained history rendering, metadata/voice lookup, and backlog clearing into
|
||||||
|
`engine/Age.Engine/Vm/VirtualMachine.TextHistory.cs`. The top-level dispatcher retains all labels and aliases
|
||||||
|
at their existing positions and routes them through guarded `StepTextHistory`; live ADV text remains with
|
||||||
|
`StepAdvText`, while skip/auto-message services remain outside both handlers. Runtime validation remains green.
|
||||||
|
|
||||||
**Gate:** no externally visible behavior or command changes; generated artifacts are byte-identical where
|
**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
|
deterministic, and the corresponding engine, Python, Godot, and corpus validations remain green after
|
||||||
each domain move.
|
each domain move.
|
||||||
@@ -1039,9 +1045,9 @@ layer's rendering diverges from ADV; save layout.
|
|||||||
## 8. Immediate next step
|
## 8. Immediate next step
|
||||||
Continue step 2 of the **codebase consolidation** maintenance slice: behavior-neutral physical splits backed
|
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`
|
by the tracked launcher and layered validation driver. With the planned `Main`, `GodotAdvHost`, and `GfxState`
|
||||||
domains isolated and the audio, movie, surface/texture, retained-object, and animation `VirtualMachine.Step`
|
domains isolated and the audio, movie, surface/texture, retained-object, animation, presentation, ADV-text, and
|
||||||
families routed through domain handlers, with retained presentation/transition dispatch now isolated as well,
|
text-history `VirtualMachine.Step` families routed through domain handlers, extract the ADV skip/auto-message
|
||||||
and ADV text-layout/rendering dispatch isolated, extract the ADV text-history opcode family next without
|
service opcode family next without replacing the proven dispatcher or changing public types, commands, and
|
||||||
replacing the proven dispatcher or changing public types, commands, and generated output.
|
generated output.
|
||||||
Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does
|
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.
|
not replace Phase B gameplay validation or the open cross-platform gates.
|
||||||
|
|||||||
72
engine/Age.Engine/Vm/VirtualMachine.TextHistory.cs
Normal file
72
engine/Age.Engine/Vm/VirtualMachine.TextHistory.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using Age.Engine.Model;
|
||||||
|
|
||||||
|
namespace Age.Engine.Vm;
|
||||||
|
|
||||||
|
public sealed partial class VirtualMachine
|
||||||
|
{
|
||||||
|
private int StepTextHistory(string label, IReadOnlyList<Operand> a, int pc)
|
||||||
|
{
|
||||||
|
switch (label)
|
||||||
|
{
|
||||||
|
case "set-text-history-recording": // 0x1bb: HISTORY.BIN suppresses recording its own UI
|
||||||
|
if (Read(a[0]) is 0 or 1)
|
||||||
|
{
|
||||||
|
bool enabled = Read(a[0]) == 1;
|
||||||
|
TextHistory.SetRecordingEnabled(enabled);
|
||||||
|
if (enabled) _host.EndTextHistoryPresentation(Gfx);
|
||||||
|
}
|
||||||
|
return pc + 1;
|
||||||
|
case "append-text-history-metadata": // 0x1d2: (metadata type, value)
|
||||||
|
TextHistory.AppendMetadata(Read(a[1]), Read(a[0]), _advTextStyle); return pc + 1;
|
||||||
|
case "step-text-history": // 0x1d0: cumulative delta from the latest retained boundary
|
||||||
|
if (TextHistory.TryStepGroup((int)Read(a[2]), out var historyEntry))
|
||||||
|
{
|
||||||
|
Write(a[0], historyEntry.LayoutSlot);
|
||||||
|
Write(a[1], historyEntry.FirstRecordIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(a[0], -1);
|
||||||
|
Write(a[1], -1);
|
||||||
|
}
|
||||||
|
return pc + 1;
|
||||||
|
case "render-text-history": // 0x1d1: rasterize/bind one retained group to a target layout
|
||||||
|
case "u0041BAE0":
|
||||||
|
{
|
||||||
|
int flags = (int)Read(a[2]);
|
||||||
|
if ((flags & 4) == 0 && TextHistory.TryBuildRenderBatch(
|
||||||
|
(int)Read(a[0]), (int)Read(a[1]), flags, Read(a[3]), Read(a[4]), out var batch))
|
||||||
|
{
|
||||||
|
batch = batch with
|
||||||
|
{
|
||||||
|
// Native History uses the text manager's current leading, not a retained-record field.
|
||||||
|
Style = batch.Style with { LineSpacing = _advTextStyle.LineSpacing }
|
||||||
|
};
|
||||||
|
_host.RenderTextHistory(
|
||||||
|
Gfx,
|
||||||
|
TextHistory.GetPresentationBinding(batch.LayoutSlot),
|
||||||
|
batch);
|
||||||
|
}
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
case "u0041BB90":
|
||||||
|
case "find-text-history-value": // 0x1d3: operand 3 is accepted but ignored natively
|
||||||
|
{
|
||||||
|
bool found = TextHistory.TryFindMetadata((int)Read(a[3]), Read(a[4]), out long value);
|
||||||
|
Write(a[0], found ? 1 : 0);
|
||||||
|
Write(a[1], value);
|
||||||
|
return pc + 1;
|
||||||
|
}
|
||||||
|
case "u0041BC00":
|
||||||
|
case "find-text-history-pair": // 0x1d4: operand 3 is accepted but ignored natively
|
||||||
|
TextHistory.TryFindVoicePair((int)Read(a[3]), out long voiceId, out long voiceArgument);
|
||||||
|
Write(a[0], voiceId);
|
||||||
|
Write(a[1], voiceArgument);
|
||||||
|
return pc + 1;
|
||||||
|
case "clear-text-history": // 0x85: bound the backlog to the current ordinary ADV block
|
||||||
|
TextHistory.Clear(); return pc + 1;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Non-text-history opcode routed to text-history handler: {label}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2258,63 +2258,17 @@ public sealed partial class VirtualMachine
|
|||||||
case "block-mark":
|
case "block-mark":
|
||||||
case "reset-message-voice-state": // 0x1bc resets native per-message voice/queued-voice state
|
case "reset-message-voice-state": // 0x1bc resets native per-message voice/queued-voice state
|
||||||
_autoVoicePending = false; return pc + 1;
|
_autoVoicePending = false; return pc + 1;
|
||||||
case "set-text-history-recording": // 0x1bb: HISTORY.BIN suppresses recording its own UI
|
case "set-text-history-recording":
|
||||||
if (Read(a[0]) is 0 or 1)
|
case "append-text-history-metadata":
|
||||||
{
|
case "step-text-history":
|
||||||
bool enabled = Read(a[0]) == 1;
|
|
||||||
TextHistory.SetRecordingEnabled(enabled);
|
|
||||||
if (enabled) _host.EndTextHistoryPresentation(Gfx);
|
|
||||||
}
|
|
||||||
return pc + 1;
|
|
||||||
case "append-text-history-metadata": // 0x1d2: (metadata type, value)
|
|
||||||
TextHistory.AppendMetadata(Read(a[1]), Read(a[0]), _advTextStyle); return pc + 1;
|
|
||||||
case "step-text-history": // 0x1d0: cumulative delta from the latest retained boundary
|
|
||||||
if (TextHistory.TryStepGroup((int)Read(a[2]), out var historyEntry))
|
|
||||||
{
|
|
||||||
Write(a[0], historyEntry.LayoutSlot);
|
|
||||||
Write(a[1], historyEntry.FirstRecordIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Write(a[0], -1);
|
|
||||||
Write(a[1], -1);
|
|
||||||
}
|
|
||||||
return pc + 1;
|
|
||||||
case "render-text-history": // 0x1d1: rasterize/bind one retained group to a target layout
|
case "render-text-history": // 0x1d1: rasterize/bind one retained group to a target layout
|
||||||
case "u0041BAE0":
|
case "u0041BAE0":
|
||||||
{
|
|
||||||
int flags = (int)Read(a[2]);
|
|
||||||
if ((flags & 4) == 0 && TextHistory.TryBuildRenderBatch(
|
|
||||||
(int)Read(a[0]), (int)Read(a[1]), flags, Read(a[3]), Read(a[4]), out var batch))
|
|
||||||
{
|
|
||||||
batch = batch with
|
|
||||||
{
|
|
||||||
// Native History uses the text manager's current leading, not a retained-record field.
|
|
||||||
Style = batch.Style with { LineSpacing = _advTextStyle.LineSpacing }
|
|
||||||
};
|
|
||||||
_host.RenderTextHistory(
|
|
||||||
Gfx,
|
|
||||||
TextHistory.GetPresentationBinding(batch.LayoutSlot),
|
|
||||||
batch);
|
|
||||||
}
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
case "u0041BB90":
|
case "u0041BB90":
|
||||||
case "find-text-history-value": // 0x1d3: operand 3 is accepted but ignored natively
|
case "find-text-history-value": // 0x1d3: operand 3 is accepted but ignored natively
|
||||||
{
|
|
||||||
bool found = TextHistory.TryFindMetadata((int)Read(a[3]), Read(a[4]), out long value);
|
|
||||||
Write(a[0], found ? 1 : 0);
|
|
||||||
Write(a[1], value);
|
|
||||||
return pc + 1;
|
|
||||||
}
|
|
||||||
case "u0041BC00":
|
case "u0041BC00":
|
||||||
case "find-text-history-pair": // 0x1d4: operand 3 is accepted but ignored natively
|
case "find-text-history-pair": // 0x1d4: operand 3 is accepted but ignored natively
|
||||||
TextHistory.TryFindVoicePair((int)Read(a[3]), out long voiceId, out long voiceArgument);
|
case "clear-text-history":
|
||||||
Write(a[0], voiceId);
|
return StepTextHistory(label, a, pc);
|
||||||
Write(a[1], voiceArgument);
|
|
||||||
return pc + 1;
|
|
||||||
case "clear-text-history": // 0x85: bound the backlog to the current ordinary ADV block
|
|
||||||
TextHistory.Clear(); return pc + 1;
|
|
||||||
case "set-font-size":
|
case "set-font-size":
|
||||||
case "set-ruby-font-size":
|
case "set-ruby-font-size":
|
||||||
case "u0041B3D0":
|
case "u0041B3D0":
|
||||||
|
|||||||
Reference in New Issue
Block a user