feat(inventory): port retail item giving (#216)
Preserve SmartBox drag-release coordinates, route the picked 3-D target through the retail AttemptPlaceIn3D policy, and send authoritative GiveObject requests with the selected stack quantity. Honor PlayerDescription's player secure-trade option and document the remaining trade-system boundary. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
a944e49b9c
commit
30d294506c
13 changed files with 421 additions and 10 deletions
|
|
@ -30,6 +30,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
private readonly Action<uint, uint>? _sendWield;
|
||||
private readonly Action<uint>? _sendDrop;
|
||||
private readonly Action<uint, uint>? _sendSplitToWorld;
|
||||
private readonly Action<uint, uint, uint>? _sendGive;
|
||||
private readonly Action<string>? _toast;
|
||||
private readonly Func<bool> _readyForInventoryRequest;
|
||||
private readonly Func<uint> _activeVendorId;
|
||||
|
|
@ -42,6 +43,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
private readonly InteractionState _interactionState;
|
||||
private readonly Func<uint> _selectedObjectId;
|
||||
private readonly StackSplitQuantityState? _stackSplitQuantity;
|
||||
private readonly Func<bool> _dragOnPlayerOpensSecureTrade;
|
||||
private readonly AutoWieldController _autoWield;
|
||||
|
||||
private long _lastUseMs = long.MinValue / 2;
|
||||
|
|
@ -72,7 +74,9 @@ public sealed class ItemInteractionController : IDisposable
|
|||
Action<uint, uint>? sendSplitToWorld = null,
|
||||
Func<uint>? selectedObjectId = null,
|
||||
StackSplitQuantityState? stackSplitQuantity = null,
|
||||
Action<uint, uint, int>? sendPutItemInContainer = null)
|
||||
Action<uint, uint, int>? sendPutItemInContainer = null,
|
||||
Action<uint, uint, uint>? sendGive = null,
|
||||
Func<bool>? dragOnPlayerOpensSecureTrade = null)
|
||||
{
|
||||
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
|
||||
_playerGuid = playerGuid ?? throw new ArgumentNullException(nameof(playerGuid));
|
||||
|
|
@ -82,6 +86,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
_sendWield = sendWield;
|
||||
_sendDrop = sendDrop;
|
||||
_sendSplitToWorld = sendSplitToWorld;
|
||||
_sendGive = sendGive;
|
||||
_nowMs = nowMs ?? (() => Environment.TickCount64);
|
||||
_toast = toast;
|
||||
_readyForInventoryRequest = readyForInventoryRequest ?? (() => true);
|
||||
|
|
@ -94,6 +99,7 @@ public sealed class ItemInteractionController : IDisposable
|
|||
_auxiliaryAction = auxiliaryAction;
|
||||
_selectedObjectId = selectedObjectId ?? (() => 0u);
|
||||
_stackSplitQuantity = stackSplitQuantity;
|
||||
_dragOnPlayerOpensSecureTrade = dragOnPlayerOpensSecureTrade ?? (() => true);
|
||||
_interactionState = interactionState ?? new InteractionState();
|
||||
_interactionState.Changed += OnInteractionModeChanged;
|
||||
_autoWield = new AutoWieldController(
|
||||
|
|
@ -310,6 +316,14 @@ public sealed class ItemInteractionController : IDisposable
|
|||
}
|
||||
|
||||
public bool DropToWorld(ItemDragPayload payload)
|
||||
=> PlaceIn3D(payload, targetGuid: 0u);
|
||||
|
||||
/// <summary>
|
||||
/// Retail inventory drag released into SmartBox. The release target is the
|
||||
/// world object under the cursor, or zero for empty ground. This is the live
|
||||
/// adapter for <c>ItemHolder::AttemptPlaceIn3D @ 0x00588600</c>.
|
||||
/// </summary>
|
||||
public bool PlaceIn3D(ItemDragPayload payload, uint targetGuid)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(payload);
|
||||
|
||||
|
|
@ -321,16 +335,17 @@ public sealed class ItemInteractionController : IDisposable
|
|||
uint fullStack = (uint)Math.Max(1, item.StackSize);
|
||||
int splitSize = (int)(_stackSplitQuantity?.GetObjectSplitSize(
|
||||
item.ObjectId, _selectedObjectId(), fullStack) ?? fullStack);
|
||||
ClientObject? target = targetGuid == 0u ? null : _objects.Get(targetGuid);
|
||||
var decision = ItemInteractionPolicy.DecidePlacement(new ItemPlacementPolicyInput(
|
||||
Snapshot(item),
|
||||
_playerGuid(),
|
||||
_groundObjectId(),
|
||||
CanMakeInventoryRequest,
|
||||
TargetId: 0,
|
||||
Target: null,
|
||||
TargetId: targetGuid,
|
||||
Target: target is null ? null : Snapshot(target),
|
||||
AllowGroundFallback: true,
|
||||
MergeAccepted: false,
|
||||
DragOnPlayerOpensSecureTrade: true,
|
||||
DragOnPlayerOpensSecureTrade: _dragOnPlayerOpensSecureTrade(),
|
||||
PlayerOnGround: _playerOnGround(),
|
||||
SplitSize: splitSize));
|
||||
ExecutePlacementActions(decision.Actions);
|
||||
|
|
@ -410,6 +425,15 @@ public sealed class ItemInteractionController : IDisposable
|
|||
// the server assigns the split-off ground stack a new guid.
|
||||
_sendSplitToWorld?.Invoke(action.ObjectId, (uint)action.Amount);
|
||||
break;
|
||||
case ItemPolicyActionKind.GiveToTarget:
|
||||
// UIAttemptGive @ 0x0058D620 sends the request and leaves
|
||||
// the source untouched until the authoritative server
|
||||
// InventoryRemoveObject / SetStackSize response arrives.
|
||||
_sendGive?.Invoke(
|
||||
action.TargetId,
|
||||
action.ObjectId,
|
||||
(uint)Math.Max(1, action.Amount));
|
||||
break;
|
||||
case ItemPolicyActionKind.Reject:
|
||||
if (!string.IsNullOrWhiteSpace(action.Message))
|
||||
_toast?.Invoke(action.Message);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue