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

@ -23,7 +23,7 @@ public enum TeleportEntryKind { Portal, Login, Death, Logout }
public enum TeleportAnimEvent
{
PlayEnterSound, // Begin(): sound_ui_enter_portal
EnterTunnel, // Off/WorldFade* -> Tunnel: world is now hidden
EnterTunnel, // First tunnel-family frame: portal viewport replaces world
Place, // Tunnel -> TunnelContinue: world loaded; place the player
PlayExitSound, // TunnelFadeOut -> WorldFadeIn: sound_ui_exit_portal
FireLoginComplete, // WorldFadeIn -> Off: send GameAction 0xA1
@ -32,7 +32,7 @@ public enum TeleportAnimEvent
/// <summary>Immutable per-frame snapshot from the sequencer.</summary>
public readonly record struct TeleportAnimSnapshot(
TeleportAnimState State,
float FadeAlpha, // 0 = clear world, 1 = full black
float ViewPlaneBlend, // 0 = game view distance, 1 = transition distance
bool ShowTunnel, // true during TunnelFadeIn..TunnelFadeOut
bool ShowPleaseWait); // true during TunnelContinue only
@ -82,6 +82,21 @@ public sealed class TeleportAnimSequencer
_enterTunnelPending = _state == TeleportAnimState.Tunnel; // true for Portal/Login/Death
}
/// <summary>
/// Cancel the current transition and discard every pending edge event.
/// Session teardown and a replacement teleport both require a clean
/// lifetime; no event from the abandoned transition may reach the next
/// session or destination.
/// </summary>
public void Reset()
{
_state = TeleportAnimState.Off;
_elapsed = 0f;
_continueElapsed = 0f;
_enterSoundPending = false;
_enterTunnelPending = false;
}
/// <summary>
/// Advance the machine by <paramref name="dt"/> seconds.
/// <paramref name="worldReady"/> = destination collision landblock is resident.
@ -104,12 +119,14 @@ public sealed class TeleportAnimSequencer
{
case TeleportAnimState.WorldFadeOut:
if (_elapsed >= FadeTime)
Advance(TeleportAnimState.TunnelFadeIn, enterTunnel: false);
// UseTime's viewport-visibility block precedes this state
// transition, so retail shows portal space on the next tick.
Advance(TeleportAnimState.TunnelFadeIn, enterTunnel: true);
break;
case TeleportAnimState.TunnelFadeIn:
if (_elapsed >= FadeTime)
Advance(TeleportAnimState.Tunnel, enterTunnel: true);
Advance(TeleportAnimState.Tunnel, enterTunnel: false);
break;
case TeleportAnimState.Tunnel:
@ -171,13 +188,13 @@ public sealed class TeleportAnimSequencer
private TeleportAnimSnapshot BuildSnapshot()
{
float alpha = ComputeFadeAlpha(_state, _elapsed);
float viewBlend = ComputeViewPlaneBlend(_state, _elapsed);
bool showTunnel = _state is TeleportAnimState.TunnelFadeIn
or TeleportAnimState.Tunnel
or TeleportAnimState.TunnelContinue
or TeleportAnimState.TunnelFadeOut;
bool pleaseWait = _state == TeleportAnimState.TunnelContinue;
return new TeleportAnimSnapshot(_state, alpha, showTunnel, pleaseWait);
return new TeleportAnimSnapshot(_state, viewBlend, showTunnel, pleaseWait);
}
/// <summary>
@ -193,21 +210,26 @@ public sealed class TeleportAnimSequencer
return RetailAnimationLevels[-negativeIndex];
}
private static float ComputeFadeAlpha(TeleportAnimState state, float elapsed)
/// <summary>
/// Retail's four states named <c>*Fade*</c> do not drive an alpha cover.
/// <c>gmSmartBoxUI::UseTime</c> (0x004D7133-0x004D725F) uses this eased
/// level only to interpolate SmartBox's view-plane distance.
/// </summary>
private static float ComputeViewPlaneBlend(TeleportAnimState state, float elapsed)
{
float t = Math.Clamp(elapsed / FadeTime, 0f, 1f);
float level = GetRetailAnimationLevel(t) / 1024f;
return state switch
{
// Fading TO black (alpha 0→1):
// Normal game view distance -> transition distance.
TeleportAnimState.WorldFadeOut => level,
TeleportAnimState.TunnelFadeIn => 1f - level, // tunnel fades IN: overlay goes clear
// Full black / fully clear inside tunnel states:
TeleportAnimState.Tunnel => 0f, // world hidden, overlay not needed
TeleportAnimState.TunnelFadeOut => level,
// Transition distance -> normal game view distance.
TeleportAnimState.TunnelFadeIn => 1f - level,
TeleportAnimState.WorldFadeIn => 1f - level,
// Stable viewport states use the ordinary game projection.
TeleportAnimState.Tunnel => 0f,
TeleportAnimState.TunnelContinue => 0f,
// Fading back out of tunnel:
TeleportAnimState.TunnelFadeOut => level, // tunnel fades out: overlay goes black
TeleportAnimState.WorldFadeIn => 1f - level, // world fades in: overlay clears
_ => 0f,
};
}