fix(client): restore retail interaction parity
All checks were successful
CI / linux-portable (push) Successful in 3m27s
CI / windows-gate (push) Successful in 6m42s
CI / release (push) Successful in 2m12s

Harden keyboard and camera routing, inventory and vendor interactions, chat/emotes, relog portal flow, and paperdoll rendering. Add retail research, connected gate coverage, and release-gate validation.
This commit is contained in:
Erik 2026-08-26 20:45:11 +02:00
parent 0c699240e0
commit f6fe0f2a4f
151 changed files with 10162 additions and 1211 deletions

View file

@ -209,6 +209,25 @@ public class RetailChaseCameraTests
Assert.Equal(Vector3.Normalize(pivot - eye), forward);
}
[Fact]
public void MapMode_TargetDirectionTransformsViewerOffsetIntoOverheadPose()
{
var pivot = new Vector3(10f, 20f, 1.5f);
float distance = MathF.Sqrt(450f * 450f + 0.75f * 0.75f);
float pitch = MathF.Atan2(0.75f, 450f);
var (eye, forward) = RetailChaseCamera.ComputeTargetDirectionPose(
pivot,
Vector3.UnitX,
distance,
pitch,
new Vector3(0f, 0.5f, -1.8f));
Assert.True(eye.Z > 430f, $"expected retail overhead eye, got Z={eye.Z}");
Assert.InRange(Vector2.Distance(new Vector2(eye.X, eye.Y), new Vector2(pivot.X, pivot.Y)), 119f, 122f);
Assert.True(forward.Z < -0.95f, $"expected steep downward view, got {forward}");
}
[Fact]
public void Basis_HorizontalHeading_IsOrthonormalAndRightHanded()
{
@ -528,6 +547,41 @@ public class RetailChaseCameraTests
Assert.Equal(RetailChaseCamera.DistanceMax, cam.Distance);
}
[Fact]
public void SetRetailFirstPersonView_PlacesEyeAheadAndLooksForward()
{
var cam = new RetailChaseCamera();
cam.SetRetailFirstPersonView();
cam.Update(
playerPosition: Vector3.Zero,
playerYaw: 0f,
playerVelocity: Vector3.Zero,
isOnGround: true,
contactPlaneNormal: Vector3.UnitZ,
dt: 1f / 60f);
Assert.True(cam.IsInHead);
Assert.Equal(new Vector3(0.18f, 0f, 1.5f), cam.Position);
Assert.Equal(1f, cam.PlayerTranslucency, 5);
var (_, forward) = RetailChaseCamera.ComputeInHeadPose(
new Vector3(0f, 0f, 1.5f),
Vector3.UnitX);
Assert.Equal(Vector3.UnitX, forward);
}
[Fact]
public void AdjustingZoomExitsRetailFirstPersonView()
{
var cam = new RetailChaseCamera();
cam.SetRetailFirstPersonView();
cam.AdjustDistance(1f);
Assert.False(cam.IsInHead);
Assert.Equal(RetailChaseCamera.DistanceMin + 1f, cam.Distance);
}
[Fact]
public void AdjustPitch_ClampsToRange()
{