fix(world): remove non-retail portal exit fade

This commit is contained in:
Erik 2026-07-15 23:20:52 +02:00
parent 842bd89c16
commit dded9e6b17
21 changed files with 919 additions and 347 deletions

View file

@ -53,6 +53,21 @@ public sealed class PortalTunnelAssetTests
Assert.Equal(DatReaderWriter.Enums.AnimationHookType.SoundTweaked, hook.HookType);
}
[Fact]
public void RequiredAssets_MissingSetupOrAnimationFailsWithActionableIds()
{
var error = Assert.Throws<InvalidOperationException>(() =>
PortalTunnelPresentation.EnsureRequiredAssets(
setupDid: 0u,
setupLoaded: false,
animationDid: 0x030005ACu,
animationLoaded: true));
Assert.Contains("required retail DAT assets unavailable", error.Message);
Assert.Contains("setup=0x00000000 (missing)", error.Message);
Assert.Contains("animation=0x030005AC (ok)", error.Message);
}
[Fact]
public void PortalTunnelCamera_RollsAroundForwardAxisWithoutLeavingPassage()
{
@ -96,7 +111,7 @@ public sealed class PortalTunnelAssetTests
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.TunnelFadeOut,
FadeAlpha: 0.5f,
ViewPlaneBlend: 0.5f,
ShowTunnel: true,
ShowPleaseWait: false));
System.Numerics.Matrix4x4 projection = controller.Apply(baseProjection);
@ -126,7 +141,7 @@ public sealed class PortalTunnelAssetTests
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.WorldFadeIn,
FadeAlpha: 1f,
ViewPlaneBlend: 1f,
ShowTunnel: false,
ShowPleaseWait: false));
System.Numerics.Matrix4x4 wideProjection = controller.Apply(baseProjection);
@ -139,7 +154,7 @@ public sealed class PortalTunnelAssetTests
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.Off,
FadeAlpha: 0f,
ViewPlaneBlend: 0f,
ShowTunnel: false,
ShowPleaseWait: false));
@ -147,6 +162,101 @@ public sealed class PortalTunnelAssetTests
Assert.Equal(baseProjection, controller.Apply(baseProjection));
}
[Fact]
public void TeleportViewPlane_ApplyToSuppliesOverrideToEveryCameraConsumer()
{
var source = new PortalTunnelCamera
{
Aspect = 16f / 9f,
FovRadians = MathF.PI / 3f,
Near = 0.1f,
Far = 5000f,
};
var controller = new TeleportViewPlaneController();
controller.Begin(source.Projection);
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.WorldFadeIn,
ViewPlaneBlend: 1f,
ShowTunnel: false,
ShowPleaseWait: false));
ICamera renderCamera = controller.ApplyTo(source);
Assert.Equal(source.View, renderCamera.View);
Assert.Equal(
TeleportViewPlaneController.TransitionViewPlaneDistance,
renderCamera.Projection.M22,
precision: 5);
renderCamera.Aspect = 4f / 3f;
Assert.Equal(4f / 3f, source.Aspect, precision: 5);
}
[Fact]
public void TeleportViewPlane_TunnelPreservesLogoutOverrideUntilTunnelContinue()
{
var capturedProjection = System.Numerics.Matrix4x4.CreatePerspectiveFieldOfView(
MathF.PI / 3f,
16f / 9f,
0.1f,
5000f);
var changedProjection = System.Numerics.Matrix4x4.CreatePerspectiveFieldOfView(
MathF.PI / 2f,
16f / 9f,
0.1f,
5000f);
var controller = new TeleportViewPlaneController();
controller.Begin(capturedProjection);
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.WorldFadeOut,
ViewPlaneBlend: 1f,
ShowTunnel: false,
ShowPleaseWait: false));
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.TunnelFadeIn,
ViewPlaneBlend: 0f,
ShowTunnel: true,
ShowPleaseWait: false));
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.Tunnel,
ViewPlaneBlend: 0f,
ShowTunnel: true,
ShowPleaseWait: false));
Assert.True(controller.Enabled);
Assert.Equal(capturedProjection.M22, controller.Apply(changedProjection).M22, precision: 5);
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.TunnelContinue,
ViewPlaneBlend: 0f,
ShowTunnel: true,
ShowPleaseWait: false));
Assert.False(controller.Enabled);
Assert.Equal(changedProjection, controller.Apply(changedProjection));
}
[Fact]
public void TeleportViewPlane_NormalPortalTunnelDoesNotEnableOverride()
{
var baseProjection = System.Numerics.Matrix4x4.CreatePerspectiveFieldOfView(
MathF.PI / 3f,
16f / 9f,
0.1f,
5000f);
var controller = new TeleportViewPlaneController();
controller.Begin(baseProjection);
controller.Update(new TeleportAnimSnapshot(
TeleportAnimState.Tunnel,
ViewPlaneBlend: 0f,
ShowTunnel: true,
ShowPleaseWait: false));
Assert.False(controller.Enabled);
Assert.Equal(baseProjection, controller.Apply(baseProjection));
}
[Theory]
[InlineData(45f)]
[InlineData(60f)]

View file

@ -60,4 +60,18 @@ public sealed class TeleportLandblockTransitionTests
Assert.False(transition.CrossesLandblock);
Assert.Equal(0x8C04FFFFu, transition.SourceLandblockId);
}
[Fact]
public void ReplacementBackToPlayerLandblock_StillRecentersAbandonedStreamingOrigin()
{
var transition = TeleportLandblockTransition.Classify(
sourceCellId: 0x8C0401ADu,
destinationCellId: 0x8C040145u,
currentStreamingCenterLandblockId: 0xA9B4FFFFu);
Assert.False(transition.CrossesLandblock);
Assert.True(transition.ChangesStreamingCenter);
Assert.Equal(0x8C04FFFFu, transition.DestinationLandblockId);
Assert.Equal(0xA9B4FFFFu, transition.StreamingCenterLandblockId);
}
}

View file

@ -0,0 +1,133 @@
using AcDream.App.Streaming;
namespace AcDream.App.Tests.Streaming;
public sealed class TeleportTransitCoordinatorTests
{
[Fact]
public void ReplacementRejectsDelayedPriorDestinationAndAcceptsCurrentAdvance()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.True(transit.QueueStart(10));
Assert.False(transit.Activate(out _));
transit.EndActive();
Assert.True(transit.QueueStart(11));
Assert.False(transit.Activate(out _));
Assert.False(transit.OfferDestination(10, true, 100, out _));
Assert.True(transit.OfferDestination(11, true, 110, out int accepted));
Assert.Equal(110, accepted);
}
[Fact]
public void PositionBeforeF751IsBufferedAndEqualFollowupCannotReplaceIt()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.False(transit.OfferDestination(11, true, 110, out _));
Assert.True(transit.QueueStart(11));
Assert.True(transit.Activate(out int buffered));
Assert.Equal(110, buffered);
Assert.False(transit.OfferDestination(11, false, 111, out _));
}
[Fact]
public void InactiveOrdinaryPositionIsNeverBuffered()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.False(transit.OfferDestination(11, false, 110, out _));
Assert.True(transit.QueueStart(11));
Assert.False(transit.Activate(out _));
}
[Fact]
public void DuplicateStartIsRejectedButSequenceWrapIsFresh()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.True(transit.QueueStart(ushort.MaxValue));
Assert.False(transit.Activate(out _));
transit.EndActive();
Assert.False(transit.CanBegin(ushort.MaxValue));
Assert.False(transit.QueueStart(ushort.MaxValue));
Assert.True(transit.CanBegin(0));
Assert.True(transit.QueueStart(0));
Assert.True(transit.HasPendingStart);
}
[Fact]
public void ExactHalfRangeUsesRetailLowerStampTieBreak()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.True(transit.QueueStart(0x8000));
Assert.False(transit.Activate(out _));
transit.EndActive();
Assert.True(transit.CanBegin(0x0000));
Assert.True(transit.QueueStart(0x0000));
}
[Fact]
public void ClearSessionAllowsSameSequenceInNewSession()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.True(transit.QueueStart(7));
Assert.False(transit.Activate(out _));
transit.ClearSession();
Assert.False(transit.IsActive);
Assert.True(transit.CanBegin(7));
}
[Fact]
public void PendingStartAcceptsEqualPositionUntilPresentationCanActivate()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.True(transit.QueueStart(11));
Assert.False(transit.OfferDestination(11, false, 110, out _));
Assert.True(transit.Activate(out int buffered));
Assert.Equal(110, buffered);
Assert.True(transit.IsActive);
}
[Fact]
public void AdvancingPositionPrunesSkippedDestination()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.False(transit.OfferDestination(11, true, 110, out _));
Assert.False(transit.OfferDestination(12, true, 120, out _));
Assert.True(transit.QueueStart(12));
Assert.True(transit.Activate(out int buffered));
Assert.Equal(120, buffered);
}
[Fact]
public void ObsoleteDestinationCannotSurviveSequenceWrap()
{
var transit = new TeleportTransitCoordinator<int>();
Assert.False(transit.OfferDestination(ushort.MaxValue, true, 999, out _));
Assert.False(transit.OfferDestination(0, true, 0, out _));
Assert.True(transit.QueueStart(0));
Assert.True(transit.Activate(out int wrapped));
Assert.Equal(0, wrapped);
transit.EndActive();
// Advance in legal retail-newer hops until 0xFFFF is current again.
Assert.True(transit.QueueStart(0x7FFF));
Assert.False(transit.Activate(out _));
transit.EndActive();
Assert.True(transit.QueueStart(0xFFFE));
Assert.False(transit.Activate(out _));
transit.EndActive();
Assert.True(transit.QueueStart(ushort.MaxValue));
Assert.False(transit.Activate(out _));
}
}