fix(ui): port retail partial stack transfers

Route the selected stack quantity through retail GetObjectSplitSize semantics, send exact split-to-container and split-to-ground actions, and keep the original object in place until the server publishes the newly guided stack. Cover selection scoping, wire bytes, container placement, and world drops with conformance tests.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-11 10:34:13 +02:00
parent 6005c51c4d
commit ea72c395c9
14 changed files with 280 additions and 19 deletions

View file

@ -18,7 +18,10 @@ public sealed class ItemInteractionControllerTests
public readonly List<(uint Source, uint Target)> UseWithTarget = new();
public readonly List<(uint Item, uint Mask)> Wields = new();
public readonly List<uint> Drops = new();
public readonly List<(uint Item, uint Amount)> SplitDrops = new();
public readonly List<string> Toasts = new();
public readonly StackSplitQuantityState SplitQuantity = new();
public uint SelectedObject;
public long Now = 1_000;
public Harness()
@ -46,7 +49,10 @@ public sealed class ItemInteractionControllerTests
sendWield: (item, mask) => Wields.Add((item, mask)),
sendDrop: Drops.Add,
nowMs: () => Now,
toast: Toasts.Add);
toast: Toasts.Add,
sendSplitToWorld: (item, amount) => SplitDrops.Add((item, amount)),
selectedObjectId: () => SelectedObject,
stackSplitQuantity: SplitQuantity);
}
public ItemInteractionController Controller { get; }
@ -389,6 +395,28 @@ public sealed class ItemInteractionControllerTests
Assert.Equal(0u, h.Objects.Get(0x50000A07u)!.ContainerId);
}
[Fact]
public void SelectedPartialStackDragOutsideUi_splitsWithoutMovingOriginal()
{
var h = new Harness();
h.AddContained(0x50000A70u, item => item.StackSize = 10);
h.SelectedObject = 0x50000A70u;
h.SplitQuantity.Reset(10u);
h.SplitQuantity.SetValue(1u);
var payload = new ItemDragPayload(
0x50000A70u,
ItemDragSource.Inventory,
SourceSlot: 0,
SourceCell: new UiItemSlot());
Assert.True(h.Controller.DropToWorld(payload));
Assert.Equal(new[] { (0x50000A70u, 1u) }, h.SplitDrops);
Assert.Empty(h.Drops);
Assert.Equal(Pack, h.Objects.Get(0x50000A70u)!.ContainerId);
Assert.Equal(10, h.Objects.Get(0x50000A70u)!.StackSize);
}
[Fact]
public void ToolbarShortcutDragOutsideUi_doesNotDropRealItem()
{