using System.Collections.Generic; using System.Linq; using Age.Engine.Model; using Age.Engine.Hosting; 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()), (0x2, System.Array.Empty()), }, System.Array.Empty()); 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); } [Fact] public void MovieMaskTransitionBlocksUntilMovieCompletionAndIgnoresClickCompletion() { var gfx = new GfxState(); var request = new MovieMaskTransitionRequest( 11, 45, 10, 1, -184, 0, 800, 600, 0, 0x325e, 0, 1000); gfx.QueueMovieMaskTransition(request); Assert.True(gfx.HasActiveForegroundTransitions(0)); Assert.True(gfx.HasActiveTimedPresentation(0)); Assert.Equal(0, gfx.CompleteForegroundTransitions(100)); Assert.False(gfx.SnapshotMovieMaskTransitions().Single().Completed); Assert.True(gfx.CompleteMovieMaskTransition(45)); Assert.False(gfx.HasActiveForegroundTransitions(100)); Assert.True(gfx.SnapshotMovieMaskTransitions().Single().Completed); } [Fact] public void ClickCompletionFinishesSurfaceAndOrdinaryOneShotsButPreservesExcludedChannels() { var gfx = new GfxState(); gfx.SetSurface(4, 0x25, -1); gfx.BindDraw(100, 4, 0, 0, 800, 600, 0, 0); gfx.BindDraw(101, 4, 0, 0, 800, 600, 0, 0); gfx.SetAnimatedObjectColorResolved(100, 0, 1000, 0, 0xffffff); gfx.SetScaleChannel(100, 0, 1000, (200, 200, 100)); gfx.SetAnimatedObjectColorResolved(101, 0, 1000, 0, 0xffffff); gfx.SetOneShotAnimationControl(101, 1); gfx.SetRotationCycle(100, 1000, (0, 0, 1)); gfx.QueueSurfaceAlphaTransition(102, 6, 101, 1, 100, 1, 0, 1000); gfx.StartForegroundTransitions(100); gfx.QueueMovieMaskTransition(new MovieMaskTransitionRequest( 11, 45, 10, 1, -184, 0, 800, 600, 0, 0x325e, 0, 1000)); Assert.True(gfx.TryCompleteClickSkippableTimedPresentation(200, out int completed)); Assert.Equal(3, completed); Assert.Equal(1.0, gfx.SnapshotForegroundTransitions(200).Single().Progress); Assert.False(gfx.TryGet(100)!.OneShotColorEnabled); Assert.False(gfx.TryGet(100)!.ScaleEnabled); Assert.True(gfx.TryGet(100)!.RotationEnabled); Assert.True(gfx.TryGet(101)!.OneShotColorEnabled); Assert.False(gfx.SnapshotMovieMaskTransitions().Single().Completed); } [Theory] [InlineData(1)] [InlineData(3)] public void AnimationServiceFlagBit0RejectsClickCompletion(long flags) { var gfx = new GfxState(); gfx.SetSurface(4, 0x25, -1); gfx.BindDraw(100, 4, 0, 0, 800, 600, 0, 0); gfx.SetAnimatedObjectColorResolved(100, 0, 1000, 0, 0xffffff); gfx.SetAnimationServiceFlags(flags); Assert.False(gfx.TryCompleteClickSkippableTimedPresentation(100, out int completed)); Assert.Equal(0, completed); Assert.True(gfx.TryGet(100)!.OneShotColorEnabled); } [Fact] public void AnimationServiceFlagBit1AcceptsWaitBypassWithoutForcingEndpoint() { var gfx = new GfxState(); gfx.SetSurface(4, 0x25, -1); gfx.BindDraw(100, 4, 0, 0, 800, 600, 0, 0); gfx.SetAnimatedObjectColorResolved(100, 0, 1000, 0, 0xffffff); gfx.SetAnimationServiceFlags(2); Assert.True(gfx.TryCompleteClickSkippableTimedPresentation(100, out int completed)); Assert.Equal(0, completed); Assert.True(gfx.TryGet(100)!.OneShotColorEnabled); } [Fact] public void MovieMaskAcceptsWaitBypassAndContinuesAsynchronously() { var gfx = new GfxState(); gfx.QueueMovieMaskTransition(new MovieMaskTransitionRequest( 11, 45, 10, 1, -184, 0, 800, 600, 0, 0x325e, 0, 1000)); Assert.True(gfx.TryCompleteClickSkippableTimedPresentation(100, out int completed)); Assert.Equal(0, completed); Assert.True(gfx.HasActiveTimedPresentation(100)); Assert.False(gfx.SnapshotMovieMaskTransitions().Single().Completed); } }