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

@ -31,16 +31,32 @@ public sealed class TeleportAnimSequencerTests
}
[Fact]
public void Snapshot_DefaultsAreOff_ClearAlpha()
public void Snapshot_DefaultsAreOff_WithGameViewPlane()
{
var snap = new TeleportAnimSnapshot(
TeleportAnimState.Off, FadeAlpha: 0f, ShowTunnel: false, ShowPleaseWait: false);
TeleportAnimState.Off, ViewPlaneBlend: 0f, ShowTunnel: false, ShowPleaseWait: false);
Assert.Equal(TeleportAnimState.Off, snap.State);
Assert.Equal(0f, snap.FadeAlpha);
Assert.Equal(0f, snap.ViewPlaneBlend);
Assert.False(snap.ShowTunnel);
Assert.False(snap.ShowPleaseWait);
}
[Fact]
public void Reset_CancelsTransitionAndDiscardsPendingEdges()
{
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Portal);
seq.Reset();
var (snap, events) = seq.Tick(0f, worldReady: true);
Assert.False(seq.IsActive);
Assert.Equal(TeleportAnimState.Off, snap.State);
Assert.Equal(0f, snap.ViewPlaneBlend);
Assert.False(snap.ShowTunnel);
Assert.Empty(events);
}
// --- Task 1.2: Begin(), IsActive, initial state ---
[Theory]
@ -285,20 +301,20 @@ public sealed class TeleportAnimSequencerTests
Assert.Contains(TeleportAnimEvent.FireLoginComplete, evts);
}
// --- Task 1.4: FadeAlpha endpoints and monotonicity ---
// --- Retail view-plane blend endpoints and monotonicity ---
[Fact]
public void FadeAlpha_IsZeroAtStart_OfWorldFadeOut()
public void ViewPlaneBlend_IsZeroAtStart_OfWorldFadeOut()
{
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Logout);
var (snap, _) = seq.Tick(dt: 0f, worldReady: false);
// At elapsed=0 in WorldFadeOut: retail animation level 0 => fully visible.
Assert.Equal(0f, snap.FadeAlpha, precision: 4);
// At elapsed=0 the override still equals the ordinary game distance.
Assert.Equal(0f, snap.ViewPlaneBlend, precision: 4);
}
[Fact]
public void FadeAlpha_IsOneAtEnd_OfWorldFadeOut()
public void ViewPlaneBlend_IsOneAtEnd_OfWorldFadeOut()
{
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Logout);
@ -309,12 +325,14 @@ public sealed class TeleportAnimSequencerTests
Assert.Equal(TeleportAnimState.WorldFadeOut, seq.State);
var (snap, _) = seq.Tick(0f, worldReady: false);
// The retail animation table should be close to fully opaque here.
Assert.True(snap.FadeAlpha > 0.95f, $"Expected FadeAlpha near 1, got {snap.FadeAlpha}");
// The retail animation table should be close to the transition distance here.
Assert.True(
snap.ViewPlaneBlend > 0.95f,
$"Expected ViewPlaneBlend near 1, got {snap.ViewPlaneBlend}");
}
[Fact]
public void FadeAlpha_IsMonotonicallyIncreasing_DuringWorldFadeOut()
public void ViewPlaneBlend_IsMonotonicallyIncreasing_DuringWorldFadeOut()
{
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Logout);
@ -327,23 +345,23 @@ public sealed class TeleportAnimSequencerTests
{
var (snap, _) = seq.Tick(step, worldReady: false);
if (seq.State != TeleportAnimState.WorldFadeOut) break;
Assert.True(snap.FadeAlpha >= prev - 0.001f,
$"Alpha decreased: {prev} -> {snap.FadeAlpha} at step {i}");
prev = snap.FadeAlpha;
Assert.True(snap.ViewPlaneBlend >= prev - 0.001f,
$"View-plane blend decreased: {prev} -> {snap.ViewPlaneBlend} at step {i}");
prev = snap.ViewPlaneBlend;
}
}
[Fact]
public void FadeAlpha_IsZero_DuringTunnelStates()
public void ViewPlaneBlend_IsZero_DuringStableTunnelStates()
{
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Portal);
seq.Tick(0f, worldReady: false);
// In Tunnel state, overlay alpha should be 0 (the tunnel viewport is shown; no alpha quad needed)
// Stable tunnel space uses the ordinary game projection.
var (snap, _) = seq.Tick(0.016f, worldReady: false);
Assert.Equal(TeleportAnimState.Tunnel, seq.State);
Assert.Equal(0f, snap.FadeAlpha);
Assert.Equal(0f, snap.ViewPlaneBlend);
}
[Fact]
@ -372,6 +390,30 @@ public sealed class TeleportAnimSequencerTests
Assert.True(snap3.ShowTunnel);
}
[Fact]
public void Logout_EntersPortalViewportOnFirstTunnelFadeInTick()
{
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Logout);
// Retail's visibility block runs before the transition block, so the
// WorldFadeOut crossing queues the viewport edge for the next tick.
var (crossing, crossingEvents) = seq.Tick(
TeleportAnimSequencer.FadeTime,
worldReady: false);
Assert.Equal(TeleportAnimState.TunnelFadeIn, crossing.State);
Assert.True(crossing.ShowTunnel);
Assert.DoesNotContain(TeleportAnimEvent.EnterTunnel, crossingEvents);
var (firstFadeInFrame, firstFadeInEvents) = seq.Tick(0f, worldReady: false);
Assert.Equal(TeleportAnimState.TunnelFadeIn, firstFadeInFrame.State);
Assert.Contains(TeleportAnimEvent.EnterTunnel, firstFadeInEvents);
var (_, tunnelEvents) = seq.Tick(TeleportAnimSequencer.FadeTime, worldReady: false);
Assert.Equal(TeleportAnimState.Tunnel, seq.State);
Assert.DoesNotContain(TeleportAnimEvent.EnterTunnel, tunnelEvents);
}
[Fact]
public void ShowPleaseWait_TrueOnlyInTunnelContinue()
{