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

@ -182,6 +182,38 @@ public class DragDropSpineTests
Assert.Equal((0x99u, 32, 32), root.DragGhostForTest);
}
[Fact]
public void RootOwnedDrag_survivesProceduralSourceCellReplacement_untilRelease()
{
var (root, list, cell) = RootWithBoundSlot(0x5001u);
object? released = null;
root.DragReleasedOutsideUi += (payload, _, _) => released = payload;
root.OnMouseDown(UiMouseButton.Left, 10, 10);
root.OnMouseMove(20, 10); // BeginDrag → root owns ghost
object payload = Assert.IsType<ItemDragPayload>(root.DragPayload);
// InventoryController.Populate/ExternalContainerController.Populate use
// UiItemList.Flush when an unrelated authoritative item update arrives.
// The old source cell is replaced while the physical button is still down.
list.Flush();
Assert.Null(cell.Parent);
Assert.Same(cell, root.DragSource);
Assert.Same(payload, root.DragPayload);
Assert.Equal((0x99u, 32, 32), root.DragGhostForTest);
Assert.Same(root, root.Captured); // retail drag element owns capture
root.OnMouseMove(600, 500);
root.OnMouseUp(UiMouseButton.Left, 600, 500);
Assert.Same(payload, released);
Assert.Null(root.DragSource);
Assert.Null(root.DragPayload);
Assert.Null(root.DragGhostForTest);
Assert.Null(root.Captured);
}
[Fact]
public void FinishDrag_overNothing_deliversNoDrop_butLiftStands()
{