fix(interaction): publish pending keyboard pickups
Route F-key pickup through the same ItemHolder backpack placement entry point as double-click activation so the inventory reserves the destination slot before the request is sent.
This commit is contained in:
parent
047a4c83b5
commit
52dbb5749e
4 changed files with 43 additions and 17 deletions
|
|
@ -69,7 +69,7 @@ Copy this block when adding a new issue:
|
||||||
|
|
||||||
## #231 — F-key pickup omits retail pending destination-slot presentation
|
## #231 — F-key pickup omits retail pending destination-slot presentation
|
||||||
|
|
||||||
**Status:** IN-PROGRESS — isolated as Slice 1 corrective commit
|
**Status:** FIXED (2026-07-21) — keyboard pickup uses the shared ItemHolder path
|
||||||
**Severity:** MEDIUM
|
**Severity:** MEDIUM
|
||||||
**Filed:** 2026-07-21
|
**Filed:** 2026-07-21
|
||||||
**Component:** selection / inventory / retained UI
|
**Component:** selection / inventory / retained UI
|
||||||
|
|
@ -98,6 +98,11 @@ destination/placement pending reservation, then sends one authoritative request.
|
||||||
Server confirmation settles it; server failure removes it without speculative
|
Server confirmation settles it; server failure removes it without speculative
|
||||||
ownership mutation.
|
ownership mutation.
|
||||||
|
|
||||||
|
**Resolution:** `ItemInteractionController.PlaceWorldItemInBackpack` now owns
|
||||||
|
the shared pending-placement notification and request dispatch. Both
|
||||||
|
double-click activation and the F-key route enter that method, so they publish
|
||||||
|
the same destination container and placement before the server request.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## #230 — Selection hits and deferred actions can cross live GUID incarnations
|
## #230 — Selection hits and deferred actions can cross live GUID incarnations
|
||||||
|
|
|
||||||
|
|
@ -13588,7 +13588,8 @@ public sealed class GameWindow : IDisposable
|
||||||
|
|
||||||
case AcDream.UI.Abstractions.Input.InputAction.SelectionPickUp:
|
case AcDream.UI.Abstractions.Input.InputAction.SelectionPickUp:
|
||||||
if (_selection.SelectedObjectId is uint pickupTarget)
|
if (_selection.SelectedObjectId is uint pickupTarget)
|
||||||
_outboundInteractions.Enqueue(() => SendPickUp(pickupTarget));
|
_outboundInteractions.Enqueue(() =>
|
||||||
|
_itemInteractionController?.PlaceWorldItemInBackpack(pickupTarget));
|
||||||
else
|
else
|
||||||
_debugVm?.AddToast("Nothing selected");
|
_debugVm?.AddToast("Nothing selected");
|
||||||
break;
|
break;
|
||||||
|
|
@ -13973,9 +13974,6 @@ public sealed class GameWindow : IDisposable
|
||||||
Console.WriteLine($"[D.5.1] toolbar use-item guid=0x{guid:X8} seq={seq}");
|
Console.WriteLine($"[D.5.1] toolbar use-item guid=0x{guid:X8} seq={seq}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendPickUp(uint itemGuid)
|
|
||||||
=> SendPickUp(itemGuid, _playerServerGuid, placement: 0);
|
|
||||||
|
|
||||||
private void SendPickUp(uint itemGuid, uint destinationContainerId, int placement)
|
private void SendPickUp(uint itemGuid, uint destinationContainerId, int placement)
|
||||||
{
|
{
|
||||||
if (_liveSession is null
|
if (_liveSession is null
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,25 @@ public sealed class ItemInteractionController : IDisposable
|
||||||
return ExecuteUseActions(decision.Actions);
|
return ExecuteUseActions(decision.Actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retail keyboard pickup entry point. <c>CPlayerSystem::PlaceInBackpack</c>
|
||||||
|
/// publishes the waiting destination slot before issuing the move request,
|
||||||
|
/// exactly like double-click pickup through ItemHolder.
|
||||||
|
/// </summary>
|
||||||
|
public bool PlaceWorldItemInBackpack(uint itemGuid)
|
||||||
|
{
|
||||||
|
if (itemGuid == 0u || _placeInBackpack is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint containerId = _backpackContainerId();
|
||||||
|
if (containerId == 0u)
|
||||||
|
containerId = _playerGuid();
|
||||||
|
const int placement = 0;
|
||||||
|
PendingBackpackPlacementRequested?.Invoke(itemGuid, containerId, placement);
|
||||||
|
_placeInBackpack(itemGuid, containerId, placement);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retail paperdoll drop entry point. The paperdoll resolves the exact
|
/// Retail paperdoll drop entry point. The paperdoll resolves the exact
|
||||||
/// target side/location; this shared interaction owner performs AutoWield's
|
/// target side/location; this shared interaction owner performs AutoWield's
|
||||||
|
|
@ -418,18 +437,7 @@ public sealed class ItemInteractionController : IDisposable
|
||||||
switch (action.Kind)
|
switch (action.Kind)
|
||||||
{
|
{
|
||||||
case ItemPolicyActionKind.PlaceInBackpack:
|
case ItemPolicyActionKind.PlaceInBackpack:
|
||||||
if (_placeInBackpack is null)
|
acted |= PlaceWorldItemInBackpack(action.ObjectId);
|
||||||
break;
|
|
||||||
uint containerId = _backpackContainerId();
|
|
||||||
if (containerId == 0u)
|
|
||||||
containerId = _playerGuid();
|
|
||||||
const int placement = 0;
|
|
||||||
PendingBackpackPlacementRequested?.Invoke(
|
|
||||||
action.ObjectId,
|
|
||||||
containerId,
|
|
||||||
placement);
|
|
||||||
_placeInBackpack(action.ObjectId, containerId, placement);
|
|
||||||
acted = true;
|
|
||||||
break;
|
break;
|
||||||
case ItemPolicyActionKind.WieldRight:
|
case ItemPolicyActionKind.WieldRight:
|
||||||
case ItemPolicyActionKind.WieldLeft:
|
case ItemPolicyActionKind.WieldLeft:
|
||||||
|
|
|
||||||
|
|
@ -1419,6 +1419,21 @@ public sealed class ItemInteractionControllerTests
|
||||||
Assert.Equal(2, h.Uses.Count);
|
Assert.Equal(2, h.Uses.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void KeyboardPickup_PublishesPendingDestinationBeforeRequest()
|
||||||
|
{
|
||||||
|
var h = new Harness();
|
||||||
|
const uint item = 0x70000A0Bu;
|
||||||
|
var pending = new List<(uint Item, uint Container, int Placement)>();
|
||||||
|
h.Controller.PendingBackpackPlacementRequested +=
|
||||||
|
(objectId, container, placement) => pending.Add((objectId, container, placement));
|
||||||
|
|
||||||
|
Assert.True(h.Controller.PlaceWorldItemInBackpack(item));
|
||||||
|
|
||||||
|
Assert.Equal(new[] { (item, Player, 0) }, pending);
|
||||||
|
Assert.Equal(new[] { (item, Player, 0) }, h.BackpackPlacements);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ResetSession_ClearsTargetBusyThrottleAndConsumedClickState()
|
public void ResetSession_ClearsTargetBusyThrottleAndConsumedClickState()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue