feat(items): port retail external-container looting
Add the ClientUISystem ground-object lifecycle, authoritative root and nested ViewContents projections, replacement and close semantics, and the DAT-authored gmExternalContainerUI strip for chests and corpses. Route double-click loot and full or partial drag transfers through the shared retail item policy without optimistic external ownership. Remove the incorrect NoLongerViewingContents behavior from owned side packs and retire AP-106/#196. Release build succeeds and all 5,875 tests pass with five intentional skips. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
48a118db91
commit
20ce67b625
27 changed files with 1750 additions and 45 deletions
|
|
@ -19,6 +19,8 @@ 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 Item, uint Container, int Placement)> Puts = new();
|
||||
public readonly List<(uint Item, uint Container, uint Placement, uint Amount)> SplitPuts = new();
|
||||
public readonly List<uint> ExternalRequests = new();
|
||||
public readonly List<uint> Drops = new();
|
||||
public readonly List<(uint Item, uint Amount)> SplitDrops = new();
|
||||
public readonly List<(uint Target, uint Item, uint Amount)> Gives = new();
|
||||
|
|
@ -28,6 +30,7 @@ public sealed class ItemInteractionControllerTests
|
|||
public uint SelectedObject;
|
||||
public bool NonCombatMode;
|
||||
public bool DragOnPlayerOpensSecureTrade = true;
|
||||
public uint GroundObject;
|
||||
public long Now = 1_000;
|
||||
|
||||
public Harness()
|
||||
|
|
@ -61,11 +64,19 @@ public sealed class ItemInteractionControllerTests
|
|||
selectedObjectId: () => SelectedObject,
|
||||
stackSplitQuantity: SplitQuantity,
|
||||
inNonCombatMode: () => NonCombatMode,
|
||||
groundObjectId: () => GroundObject,
|
||||
sendPutItemInContainer: (item, container, placement) =>
|
||||
Puts.Add((item, container, placement)),
|
||||
sendGive: (target, item, amount) => Gives.Add((target, item, amount)),
|
||||
dragOnPlayerOpensSecureTrade: () => DragOnPlayerOpensSecureTrade,
|
||||
systemMessage: SystemMessages.Add);
|
||||
systemMessage: SystemMessages.Add,
|
||||
sendSplitToContainer: (item, container, placement, amount) =>
|
||||
SplitPuts.Add((item, container, placement, amount)),
|
||||
requestExternalContainer: id =>
|
||||
{
|
||||
GroundObject = id;
|
||||
ExternalRequests.Add(id);
|
||||
});
|
||||
}
|
||||
|
||||
public ItemInteractionController Controller { get; }
|
||||
|
|
@ -103,6 +114,51 @@ public sealed class ItemInteractionControllerTests
|
|||
Assert.Empty(h.UseWithTarget);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExternalContainerUse_RequestsGroundObjectAndSendsUse()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint chest = 0x70000001u;
|
||||
h.Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = chest,
|
||||
Type = ItemType.Container,
|
||||
ItemsCapacity = 24,
|
||||
Useability = ItemUseability.Remote,
|
||||
});
|
||||
|
||||
Assert.True(h.Controller.ActivateItem(chest));
|
||||
|
||||
Assert.Equal(new uint[] { chest }, h.Uses);
|
||||
Assert.Equal(new uint[] { chest }, h.ExternalRequests);
|
||||
Assert.Equal(chest, h.GroundObject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DropOwnedItemOnOpenExternalContainer_SendsPutWithoutOptimisticMove()
|
||||
{
|
||||
var h = new Harness();
|
||||
const uint itemId = 0x50000A33u;
|
||||
const uint chest = 0x70000001u;
|
||||
ClientObject item = h.AddContained(itemId);
|
||||
h.Objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = chest,
|
||||
Type = ItemType.Container,
|
||||
ItemsCapacity = 24,
|
||||
PublicWeenieBitfield = (uint)PublicWeenieFlags.Openable,
|
||||
});
|
||||
h.GroundObject = chest;
|
||||
var cell = new UiItemSlot();
|
||||
cell.SetItem(itemId, 0u);
|
||||
var payload = new ItemDragPayload(itemId, ItemDragSource.Inventory, 0, cell);
|
||||
|
||||
Assert.True(h.Controller.PlaceIn3D(payload, chest));
|
||||
|
||||
Assert.Equal(new[] { (itemId, chest, 0) }, h.Puts);
|
||||
Assert.Equal(Pack, item.ContainerId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrimaryClickRouter_distinguishesInactiveSuccessAndConsumedRejection()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue