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:
Erik 2026-07-11 09:46:32 +02:00
parent 3281e0acb4
commit dc1649c493
19 changed files with 479 additions and 28 deletions

View file

@ -92,8 +92,8 @@ public static class ShortcutDropPlanner
/// <summary>
/// Plan the toolbar side of <c>RecvNotice_FullMergingItem @ 0x004BE9B0</c>.
/// The normal retail invariant is one object shortcut; all matching records are
/// rekeyed here so a malformed duplicate cannot retain a destroyed stack id.
/// <c>CreateShortcutToItem</c> removes an existing destination shortcut before
/// placing it into the source shortcut's former slot.
/// </summary>
public static ShortcutMutation[] PlanFullStackMerge(
IReadOnlyList<ShortcutEntry?> slots,
@ -104,15 +104,21 @@ public static class ShortcutDropPlanner
if (slots.Count != ShortcutStore.SlotCount || oldObjectId == 0 || newObjectId == 0)
return Array.Empty<ShortcutMutation>();
var mutations = new List<ShortcutMutation>();
var state = new ShortcutEntry?[ShortcutStore.SlotCount];
for (int slot = 0; slot < slots.Count; slot++)
{
if (VisibleEntry(slots[slot]) is not { } entry || entry.ObjectId != oldObjectId)
continue;
mutations.Add(ShortcutMutation.Remove(slot));
mutations.Add(ShortcutMutation.Add(entry with { ObjectId = newObjectId }));
state[slot] = slots[slot];
}
var mutations = new List<ShortcutMutation>(3);
int sourceSlot = FindFirstVisibleObject(state, oldObjectId);
if (sourceSlot < 0)
return Array.Empty<ShortcutMutation>();
ShortcutEntry sourceEntry = state[sourceSlot]!.Value;
RemoveVisibleAt(state, sourceSlot, mutations);
RemoveFirstVisibleObject(state, newObjectId, mutations);
Add(state, sourceEntry with { ObjectId = newObjectId }, mutations);
return mutations.ToArray();
}
@ -149,6 +155,14 @@ public static class ShortcutDropPlanner
}
}
private static int FindFirstVisibleObject(ShortcutEntry?[] state, uint objectId)
{
for (int slot = 0; slot < state.Length; slot++)
if (VisibleEntry(state[slot]) is { } entry && entry.ObjectId == objectId)
return slot;
return -1;
}
private static void Add(
ShortcutEntry?[] state,
ShortcutEntry entry,