feat(ui): complete retail item drop branches
Give retained buttons a reusable item-drop seam, wire the toolbar backpack target, and port retail stack-merge legality, capacity clamping, wire dispatch, destination selection, and immediate shortcut rekey notice. Record the live ACE merge gate and keep split quantity under AP-101. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
3281e0acb4
commit
dc1649c493
19 changed files with 479 additions and 28 deletions
|
|
@ -58,6 +58,8 @@ public sealed class InventoryController : IItemListDragHandler, IRetainedPanelCo
|
|||
private readonly Action<uint>? _sendUse;
|
||||
private readonly Action<uint>? _sendNoLongerViewing;
|
||||
private readonly Action<uint, uint, int>? _sendPutItemInContainer; // (item, container, placement)
|
||||
private readonly Action<uint, uint, uint>? _sendStackableMerge;
|
||||
private readonly Action<uint, uint>? _notifyMergeAttempt;
|
||||
private readonly ItemInteractionController? _itemInteraction;
|
||||
private bool _disposed;
|
||||
|
||||
|
|
@ -80,6 +82,8 @@ public sealed class InventoryController : IItemListDragHandler, IRetainedPanelCo
|
|||
Action<uint>? sendUse,
|
||||
Action<uint>? sendNoLongerViewing,
|
||||
Action<uint, uint, int>? sendPutItemInContainer,
|
||||
Action<uint, uint, uint>? sendStackableMerge,
|
||||
Action<uint, uint>? notifyMergeAttempt,
|
||||
ItemInteractionController? itemInteraction,
|
||||
Action? onClose)
|
||||
{
|
||||
|
|
@ -91,6 +95,8 @@ public sealed class InventoryController : IItemListDragHandler, IRetainedPanelCo
|
|||
_sendUse = sendUse;
|
||||
_sendNoLongerViewing = sendNoLongerViewing;
|
||||
_sendPutItemInContainer = sendPutItemInContainer;
|
||||
_sendStackableMerge = sendStackableMerge;
|
||||
_notifyMergeAttempt = notifyMergeAttempt;
|
||||
_itemInteraction = itemInteraction;
|
||||
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
||||
|
||||
|
|
@ -197,12 +203,15 @@ public sealed class InventoryController : IItemListDragHandler, IRetainedPanelCo
|
|||
Action<uint>? sendUse = null,
|
||||
Action<uint>? sendNoLongerViewing = null,
|
||||
Action<uint, uint, int>? sendPutItemInContainer = null,
|
||||
Action<uint, uint, uint>? sendStackableMerge = null,
|
||||
Action<uint, uint>? notifyMergeAttempt = null,
|
||||
ItemInteractionController? itemInteraction = null,
|
||||
Action? onClose = null)
|
||||
=> new InventoryController(layout, objects, playerGuid, iconIds, strength, selection,
|
||||
ownerName, datFont,
|
||||
contentsEmptySprite, sideBagEmptySprite, mainPackEmptySprite,
|
||||
sendUse, sendNoLongerViewing, sendPutItemInContainer, itemInteraction,
|
||||
sendUse, sendNoLongerViewing, sendPutItemInContainer,
|
||||
sendStackableMerge, notifyMergeAttempt, itemInteraction,
|
||||
onClose);
|
||||
|
||||
private void OnObjectChanged(ClientObject o) { if (Concerns(o)) Populate(); }
|
||||
|
|
@ -417,6 +426,14 @@ public sealed class InventoryController : IItemListDragHandler, IRetainedPanelCo
|
|||
uint item = payload.ObjId;
|
||||
if (item == 0) return;
|
||||
|
||||
// UIElement_ItemList::AcceptDragObject tries ItemHolder::AttemptMerge first
|
||||
// when a physical item is released on an occupied inventory cell. A legal
|
||||
// merge consumes the drop; an illegal merge falls through to insert/move.
|
||||
if (targetList == _contentsGrid
|
||||
&& targetCell.ItemId != 0
|
||||
&& TryMergeStacks(item, targetCell.ItemId))
|
||||
return;
|
||||
|
||||
uint container; int placement;
|
||||
if (targetList == _contentsGrid)
|
||||
{
|
||||
|
|
@ -439,6 +456,39 @@ public sealed class InventoryController : IItemListDragHandler, IRetainedPanelCo
|
|||
_sendPutItemInContainer?.Invoke(item, container, placement);
|
||||
}
|
||||
|
||||
private bool TryMergeStacks(uint sourceId, uint targetId)
|
||||
{
|
||||
if (_sendStackableMerge is null
|
||||
|| _objects.Get(sourceId) is not { } source
|
||||
|| _objects.Get(targetId) is not { } target)
|
||||
return false;
|
||||
|
||||
StackMergePlan? plan = StackMergePlanner.Plan(
|
||||
new StackMergeItem(
|
||||
source.ObjectId,
|
||||
source.WeenieClassId,
|
||||
source.StackSize,
|
||||
source.StackSizeMax,
|
||||
source.TradeState),
|
||||
new StackMergeItem(
|
||||
target.ObjectId,
|
||||
target.WeenieClassId,
|
||||
target.StackSize,
|
||||
target.StackSizeMax,
|
||||
target.TradeState),
|
||||
_itemInteraction?.CanMakeInventoryRequest ?? true,
|
||||
requestedAmount: Math.Max(1, source.StackSize));
|
||||
if (plan is not { } merge)
|
||||
return false;
|
||||
|
||||
_sendStackableMerge(merge.SourceObjectId, merge.TargetObjectId, merge.Amount);
|
||||
// ItemHolder::AttemptMerge broadcasts this immediately after UIAttemptMerge;
|
||||
// it is an attempt notice, not a later server-confirm event.
|
||||
_notifyMergeAttempt?.Invoke(merge.SourceObjectId, merge.TargetObjectId);
|
||||
_selection.Select(merge.TargetObjectId, SelectionChangeSource.Inventory);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>True only when we KNOW the container is full (capacity known + contents indexed). A
|
||||
/// closed bag (unknown count) returns false → advisory accept; the server is authoritative.</summary>
|
||||
private bool IsContainerFull(uint container)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue