fix(runtime): align portal and movement presentation
Port retail portal viewport projection and reveal behavior, preserve outbound combat style, drive remote and local grounded movement from authored CSequence root frames, and reuse the local prepared pose so animation hooks advance once. User-verified portal, observer movement, combat stance, and short-tap locomotion gates. Release build passed with 5,767 tests and five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
e95f55f25b
commit
124e046976
30 changed files with 1362 additions and 348 deletions
|
|
@ -262,6 +262,70 @@ public sealed class TeleportAnimSequencerTests
|
|||
Assert.Equal(expected, TeleportAnimSequencer.GetRetailAnimationLevel(t));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(240f)]
|
||||
[InlineData(700f)]
|
||||
[InlineData(2000f)]
|
||||
public void WorldFadeOut_DoesNotPublishTerminalProjectionOnOutgoingWorld(float framesPerSecond)
|
||||
{
|
||||
var seq = new TeleportAnimSequencer();
|
||||
seq.Begin(TeleportEntryKind.Logout);
|
||||
seq.Tick(0f, worldReady: false);
|
||||
|
||||
AssertOutgoingViewportRetiresBeforeTerminal(
|
||||
seq,
|
||||
TeleportAnimState.WorldFadeOut,
|
||||
framesPerSecond);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(240f)]
|
||||
[InlineData(700f)]
|
||||
[InlineData(2000f)]
|
||||
public void TunnelFadeOut_DoesNotPublishTerminalProjectionOnFiniteTunnel(float framesPerSecond)
|
||||
{
|
||||
var seq = new TeleportAnimSequencer();
|
||||
seq.Begin(TeleportEntryKind.Portal);
|
||||
seq.Tick(0f, worldReady: false);
|
||||
seq.Tick(0.016f, worldReady: true);
|
||||
DriveSeconds(
|
||||
seq,
|
||||
TeleportAnimSequencer.MinContinue + 0.05f,
|
||||
worldReady: true,
|
||||
step: 1f / framesPerSecond);
|
||||
Assert.Equal(TeleportAnimState.TunnelFadeOut, seq.State);
|
||||
|
||||
AssertOutgoingViewportRetiresBeforeTerminal(
|
||||
seq,
|
||||
TeleportAnimState.TunnelFadeOut,
|
||||
framesPerSecond);
|
||||
}
|
||||
|
||||
private static void AssertOutgoingViewportRetiresBeforeTerminal(
|
||||
TeleportAnimSequencer seq,
|
||||
TeleportAnimState outgoingState,
|
||||
float framesPerSecond)
|
||||
{
|
||||
float dt = 1f / framesPerSecond;
|
||||
bool transitioned = false;
|
||||
for (int i = 0; i < (int)MathF.Ceiling(framesPerSecond * 1.1f); i++)
|
||||
{
|
||||
var (snapshot, _) = seq.Tick(dt, worldReady: true);
|
||||
if (snapshot.State == outgoingState)
|
||||
{
|
||||
Assert.True(
|
||||
snapshot.ViewPlaneBlend <= 1022f / 1024f,
|
||||
$"Published unsafe outgoing blend {snapshot.ViewPlaneBlend}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
transitioned = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Assert.True(transitioned, "The outgoing viewport did not retire.");
|
||||
}
|
||||
|
||||
// --- TunnelFadeOut -> WorldFadeIn: PlayExitSound edge event ---
|
||||
|
||||
[Fact]
|
||||
|
|
@ -314,14 +378,14 @@ public sealed class TeleportAnimSequencerTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void ViewPlaneBlend_IsOneAtEnd_OfWorldFadeOut()
|
||||
public void ViewPlaneBlend_ReachesLastVisibleLevel_BeforeWorldFadeOutRetires()
|
||||
{
|
||||
var seq = new TeleportAnimSequencer();
|
||||
seq.Begin(TeleportEntryKind.Logout);
|
||||
seq.Tick(0f, worldReady: false); // consume enter-sound tick, elapsed≈0
|
||||
|
||||
// Drive to just BEFORE the transition (so we're still in WorldFadeOut)
|
||||
DriveSeconds(seq, TeleportAnimSequencer.FadeTime - 0.02f, worldReady: false);
|
||||
DriveSeconds(seq, TeleportAnimSequencer.FadeTime - 0.03f, worldReady: false);
|
||||
Assert.Equal(TeleportAnimState.WorldFadeOut, seq.State);
|
||||
|
||||
var (snap, _) = seq.Tick(0f, worldReady: false);
|
||||
|
|
@ -329,6 +393,7 @@ public sealed class TeleportAnimSequencerTests
|
|||
Assert.True(
|
||||
snap.ViewPlaneBlend > 0.95f,
|
||||
$"Expected ViewPlaneBlend near 1, got {snap.ViewPlaneBlend}");
|
||||
Assert.True(snap.ViewPlaneBlend <= 1022f / 1024f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue