engine: retain History and wait presentation

This commit is contained in:
gamer147
2026-07-30 19:21:38 -04:00
parent 1367563485
commit ef19ab79e0
14 changed files with 585 additions and 95 deletions

View File

@@ -388,6 +388,34 @@ public class AdvTextOpsTests
Assert.Single(host.WaitIndicators));
}
[Fact]
public void WaitIndicatorHandlePublishesItsResolvedLayoutBindingToHost()
{
var table = OpcodeTableJson.Load(Paths.OpcodesJson);
static Operand I(long value) => new(0, value);
var script = ScriptAssembler.Assemble(table, "WAITMARK_BINDING",
new List<(int, Operand[])>
{
(0x70, new[] { I(1), I(800), I(160), I(0), I(430) }),
(0x79, new[] { I(1), I(100), I(47) }),
(0x213, new[] { I(1), I(0xd6d8), I(500) }),
(0x212, new[] { I(1), I(0xd674) }),
(0x2, Array.Empty<Operand>()),
}, Array.Empty<string>());
var host = new RecordingHost();
new VirtualMachine(script, table, host).Run();
var retained = Assert.Single(host.WaitIndicatorBindings);
Assert.Equal(
new AdvTextLayoutPresentationBinding(
1, 21, 0xd6d8, 500, 0xd674, 100, 47),
retained.Binding);
Assert.Equal(
new AdvTextLayoutSnapshot(1, 800, 160, 0, 430, 0, 0, 800, 160),
retained.Layout);
}
[Fact]
public void WaitIndicatorTerminalFrameIsExclusive()
{

View File

@@ -45,6 +45,10 @@ public class HistoryPresentationOpsTests
Assert.Equal((24, 0xffffffL, 0x606060L, 8, " 明朝"),
(render.Style.PrimaryFontSize, render.Style.TextColor, render.Style.EffectColor,
render.Style.LineSpacing, render.Style.FontFace));
var retained = Assert.Single(host.RetainedHistoryRenders);
Assert.Equal(4, retained.Binding.LayoutSlot);
Assert.Equal(0x18, retained.Binding.SourceSurfaceSlot);
Assert.Same(render, retained.Batch);
}
[Fact]

View File

@@ -0,0 +1,94 @@
using Age.Engine.Hosting;
using Age.Engine.Model;
using Age.Engine.Text;
public class RetainedAdvWaitIndicatorPresentationTests
{
private static readonly AdvWaitIndicatorConfig Config = new(
LayoutSlot: 1,
X: 385,
Y: 140,
SurfaceSlot: 12,
SourceX: 0,
SourceY: 0,
CellWidth: 30,
CellHeight: 27,
TerminalFrame: 12,
FramePeriodMs: 48);
private static readonly AdvTextLayoutPresentationBinding Binding = new(
LayoutSlot: 1,
SourceSurfaceSlot: 21,
FirstObjectHandle: 0xd6d8,
ObjectCapacity: 500,
WaitIndicatorObjectHandle: 0xd674,
ResetCursorX: 100,
ResetCursorY: 47);
[Fact]
public void PublishesAtlasCellAtLayoutRelativePosition()
{
var presentation = new RetainedAdvWaitIndicatorPresentation(
Config, Binding, layoutOriginX: 0, layoutOriginY: 430);
var gfx = new GfxState();
gfx.SetSurface(12, 0x337c, 0xff00);
Assert.True(presentation.Update(gfx, elapsedMs: 96, visible: true));
RenderObject marker = Assert.Single(gfx.SnapshotVisibleObjects());
Assert.Equal(
(0xd674L, 12, 60, 0, 30, 27, 385, 570),
(marker.Handle, gfx.TryGet(marker.Handle)!.SourceSlot, marker.SrcX, marker.SrcY,
marker.W, marker.H, marker.DstX, marker.DstY));
Assert.Equal(2, presentation.PublishedFrame);
}
[Fact]
public void MutatesOnlyWhenFrameChangesAndErasesWhenHidden()
{
var presentation = new RetainedAdvWaitIndicatorPresentation(
Config, Binding, 0, 430);
var gfx = new GfxState();
Assert.True(presentation.Update(gfx, 0, visible: true));
Assert.False(presentation.Update(gfx, 47, visible: true));
Assert.True(presentation.Update(gfx, 48, visible: true));
Assert.Equal(30, Assert.Single(gfx.SnapshotVisibleObjects()).SrcX);
Assert.True(presentation.Update(gfx, 48, visible: false));
Assert.Empty(gfx.SnapshotVisibleObjects());
Assert.False(presentation.Update(gfx, 48, visible: false));
}
[Fact]
public void RepublishRestoresAHandleErasedByScript()
{
var presentation = new RetainedAdvWaitIndicatorPresentation(
Config, Binding, 0, 430);
var gfx = new GfxState();
presentation.Update(gfx, 144, visible: true);
gfx.EraseRange(Binding.WaitIndicatorObjectHandle, 1);
Assert.True(presentation.Republish(gfx, 144, visible: true));
RenderObject marker = Assert.Single(gfx.SnapshotVisibleObjects());
Assert.Equal((0xd674L, 90), (marker.Handle, marker.SrcX));
}
[Fact]
public void RejectsMissingHandleAndMismatchedLayout()
{
Assert.Throws<ArgumentException>(() =>
new RetainedAdvWaitIndicatorPresentation(
Config,
Binding with { WaitIndicatorObjectHandle = -1 },
0,
430));
Assert.Throws<ArgumentException>(() =>
new RetainedAdvWaitIndicatorPresentation(
Config with { LayoutSlot = 2 },
Binding,
0,
430));
}
}

View File

@@ -21,6 +21,8 @@ internal class RecordingHost : IHost
public readonly List<(int Slot, int X, int Y)> TextCursors = new();
public readonly List<(int Surface, int X, int Y, string Text)> SurfaceStrings = new();
public readonly List<AdvTextHistoryRenderBatch> HistoryRenders = new();
public readonly List<(AdvTextLayoutPresentationBinding Binding, AdvTextHistoryRenderBatch Batch)>
RetainedHistoryRenders = new();
public readonly Dictionary<int, AdvTextHistoryRenderBatch> ActiveHistoryRenders = new();
public int HistoryPresentationEnds;
public readonly List<int> ClearedTextLayouts = new();
@@ -30,6 +32,8 @@ internal class RecordingHost : IHost
public readonly List<(int Slot, int SourceX, int SourceY, int Width, int Height, int X, int Y)>
TextureDraws = new();
public readonly List<AdvWaitIndicatorConfig> WaitIndicators = new();
public readonly List<(AdvTextLayoutPresentationBinding Binding, AdvTextLayoutSnapshot Layout)>
WaitIndicatorBindings = new();
public readonly List<bool> WaitIndicatorEnabledChanges = new();
public readonly List<int> PublishedAdvTextLayouts = new();
public readonly List<long> SleptDurations = new();
@@ -129,11 +133,21 @@ internal class RecordingHost : IHost
HistoryRenders.Add(batch);
ActiveHistoryRenders[batch.LayoutSlot] = batch;
}
public bool RenderTextHistory(
GfxState gfx,
AdvTextLayoutPresentationBinding binding,
AdvTextHistoryRenderBatch batch)
{
RetainedHistoryRenders.Add((binding, batch));
RenderTextHistory(batch);
return false;
}
public void EndTextHistoryPresentation()
{
HistoryPresentationEnds++;
ActiveHistoryRenders.Clear();
}
public void EndTextHistoryPresentation(GfxState gfx) => EndTextHistoryPresentation();
public int MessageWindowAlphaSetting { get; set; }
public void SetMessageWindowAlphaSetting(int value) => MessageWindowAlphaSetting = value;
public void FillSurfaceRect(SurfaceRectFill fill) => SurfaceFills.Add(fill);
@@ -141,6 +155,10 @@ internal class RecordingHost : IHost
public void PresentObjectRange(GfxState gfx, long firstHandle, long count)
=> PresentedRanges.Add((firstHandle, count));
public void ConfigureAdvWaitIndicator(AdvWaitIndicatorConfig config) => WaitIndicators.Add(config);
public void BindAdvWaitIndicator(
AdvTextLayoutPresentationBinding binding,
AdvTextLayoutSnapshot layout)
=> WaitIndicatorBindings.Add((binding, layout));
public void SetAdvWaitIndicatorEnabled(bool enabled) => WaitIndicatorEnabledChanges.Add(enabled);
public void PublishAdvTextLayout(int layoutSlot) => PublishedAdvTextLayouts.Add(layoutSlot);
public void SetAdvPagePresentationSuspended(bool suspended)