Implement ADV foreground transition lifecycle

This commit is contained in:
gamer147
2026-07-10 18:49:53 -04:00
parent bb16a5496a
commit 7f900c8fc6
16 changed files with 543 additions and 54 deletions

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Linq;
using Age.Engine.Model;
using Age.Engine.Sys4;
using Age.Engine.Vm;
using Xunit;
public class ForegroundTransitionTests
{
private static OpcodeTable T() => OpcodeTableJson.Load(Paths.OpcodesJson);
private static Operand G(int address) => new(3, address);
private static Operand I(long value) => new(0, value);
[Fact]
public void SurfaceTransition_HasStartDelayProgressNaturalAndForcedCompletion()
{
var gfx = new GfxState();
gfx.SetSurface(4, 0x25, -1);
gfx.BindDraw(100, 4, 0, 0, 800, 600, 0, 0);
gfx.SetRotationCycle(100, 1000, (0, 0, 1)); // ambient, deliberately independent
gfx.QueueSurfaceAlphaTransition(102, 6, 101, 1, 100, 1, 100, 200);
Assert.Equal(1, gfx.StartForegroundTransitions(1000));
Assert.Equal(0.0, gfx.SnapshotForegroundTransitions(1050).Single().Progress);
Assert.Equal(0.25, gfx.SnapshotForegroundTransitions(1150).Single().Progress, 3);
Assert.False(gfx.HasActiveForegroundTransitions(1300));
Assert.True(gfx.TryGet(100)!.RotationEnabled);
gfx.QueueSurfaceAlphaTransition(202, 7, 201, 1, 200, 1, 0, 500);
gfx.StartForegroundTransitions(2000);
Assert.True(gfx.HasActiveForegroundTransitions(2100));
Assert.Equal(1, gfx.CompleteForegroundTransitions(2100));
var forced = gfx.SnapshotForegroundTransitions(2100).Single(t => t.TargetSlot == 7);
Assert.True(forced.Forced);
Assert.Equal(1.0, forced.Progress);
Assert.True(gfx.TryGet(100)!.RotationEnabled); // transition click did not finish ambient animation
}
[Fact]
public void CloneObject_SnapshotsOldSurfaceBindingForTransitionRangeA()
{
var gfx = new GfxState();
gfx.SetSurface(4, 0x25, -1);
gfx.BindDraw(100, 4, 0, 0, 800, 600, 0, 0);
gfx.SetStaticObjectColorResolved(100, 2, -1, -1);
Assert.True(gfx.CloneObject(100, 101));
gfx.SetSurface(5, 0x27, -1);
gfx.BindDraw(100, 5, 0, 0, 800, 600, 0, 0);
var snapshot = gfx.SnapshotVisibleObjects();
Assert.Equal(0x27, snapshot.Single(x => x.Handle == 100).SurfaceResId);
Assert.Equal(0x25, snapshot.Single(x => x.Handle == 101).SurfaceResId);
}
[Fact]
public void VmDispatchesSkipQueriesQueueAndPresentResumeBoundary()
{
var table = T();
var scene = ScriptAssembler.Assemble(table, "FOREGROUND", new List<(int, Operand[])>
{
(0x1c7, new[] { G(1) }),
(0x1cc, new[] { G(2) }),
(0x223, new[] { I(102), I(6), I(101), I(1), I(100), I(1), I(50), I(200) }),
(0x21c, System.Array.Empty<Operand>()),
(0x2, System.Array.Empty<Operand>()),
}, System.Array.Empty<string>());
var host = new RecordingHost { MessageSkip = true, AdvReadSkip = true };
var vm = new VirtualMachine(scene, table, host);
vm.Run();
Assert.Equal(1, vm.Globals[1]);
Assert.Equal(1, vm.Globals[2]);
Assert.Equal(1, host.TransitionWaits);
Assert.Equal(1.0, vm.Gfx.SnapshotForegroundTransitions(100).Single().Progress);
}
}

View File

@@ -4,6 +4,27 @@ using Xunit;
public class RenderObjectBlendTests
{
[Fact]
public void NegativeColorOperands_PreserveCurrentStaticColor()
{
var g = new GfxState();
g.SetObjectColor(0x100, GfxState.PackColor(0, 0xffffff));
g.SetObjectColorResolved(0x100, -1, -1);
Assert.Equal(GfxState.PackColor(0, 0xffffff), g.TryGet(0x100)!.Color);
}
[Fact]
public void TransitionColorMode_TreatsWhiteAsIdentityAndAlphaAsOpacity()
{
var g = new GfxState();
g.SetSurface(4, 1, -1);
g.BindDraw(0x100, 4, 0, 0, 1, 1, 0, 0);
g.SetStaticObjectColorResolved(0x100, 2, -1, -1);
var rendered = g.SnapshotVisibleObjects().Single();
Assert.Equal(255, rendered.Alpha);
Assert.Equal(0, rendered.TintStrength);
}
// Make a visible textured object bound to a slot that has a surface, so it appears in the snapshot.
private static GfxState WithVisibleObject(long handle, long resId, long colorKey)
{

View File

@@ -9,12 +9,30 @@ using Age.Engine.Model;
internal sealed class RecordingHost : IHost
{
public int Waits;
public int Presents;
public int TransitionWaits;
public bool MessageSkip;
public bool AdvReadSkip;
public readonly List<(int Offset, string Text)> Lines = new();
public readonly List<long> SleptDurations = new();
public void ShowText(int offset, string text) => Lines.Add((offset, text));
public void WaitForInput() => Waits++;
public void Sleep(long duration) => SleptDurations.Add(duration);
public void FrameYield() { }
public bool IsMessageSkipActive => MessageSkip;
public bool IsAdvReadSkipActive => AdvReadSkip;
public void PresentFrame(GfxState gfx)
{
Presents++;
gfx.StartForegroundTransitions(100);
gfx.CompleteForegroundTransitions(100);
}
public void WaitForForegroundTransition(GfxState gfx)
{
TransitionWaits++;
gfx.StartForegroundTransitions(100);
gfx.CompleteForegroundTransitions(100);
}
public void CreateTexture(int slot, int w, int h) { }
public void SetTexture(long resId, int slot) { }
public void DrawTexture(int slot, int sx, int sy, int w, int h, int dx, int dy) { }