fix(client): restore retail interaction parity
Harden keyboard and camera routing, inventory and vendor interactions, chat/emotes, relog portal flow, and paperdoll rendering. Add retail research, connected gate coverage, and release-gate validation.
This commit is contained in:
parent
0c699240e0
commit
f6fe0f2a4f
151 changed files with 10162 additions and 1211 deletions
|
|
@ -43,6 +43,7 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
private readonly UiItemList _contentsList;
|
||||
|
||||
private uint _openContainer;
|
||||
private PendingBackpackPlacement? _pendingPlacement;
|
||||
private bool _closeRequested;
|
||||
private bool _disposed;
|
||||
|
||||
|
|
@ -115,6 +116,9 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
_objects.Cleared += OnObjectsCleared;
|
||||
_selection.Changed += OnSelectionChanged;
|
||||
_itemInteraction.StateChanged += OnInteractionStateChanged;
|
||||
_itemInteraction.PendingBackpackPlacementRequested += OnPendingPlacementRequested;
|
||||
_itemInteraction.PendingBackpackPlacementCancelled += OnPendingPlacementCancelled;
|
||||
_itemInteraction.PendingBackpackPlacementResolved += OnPendingPlacementResolved;
|
||||
ClearLists();
|
||||
}
|
||||
|
||||
|
|
@ -210,13 +214,14 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
UiItemSlot targetCell,
|
||||
ItemDragPayload payload)
|
||||
{
|
||||
if (payload.SourceKind == ItemDragSource.ShortcutBar)
|
||||
return ItemDragAcceptance.None;
|
||||
if (!ReferenceEquals(targetList, _contentsList)
|
||||
|| payload.SourceKind == ItemDragSource.ShortcutBar
|
||||
|| payload.ObjId == 0u
|
||||
|| _openContainer == 0u
|
||||
|| payload.ObjId == _openContainer)
|
||||
|| _openContainer == 0u)
|
||||
return ItemDragAcceptance.Reject;
|
||||
return ItemDragAcceptance.Accept;
|
||||
return EvaluateDrop(payload.ObjId) == InventoryContainerPlacementRejection.None
|
||||
? ItemDragAcceptance.Accept
|
||||
: ItemDragAcceptance.Reject;
|
||||
}
|
||||
|
||||
public void HandleDropRelease(
|
||||
|
|
@ -224,8 +229,19 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
UiItemSlot targetCell,
|
||||
ItemDragPayload payload)
|
||||
{
|
||||
if (OnDragOver(targetList, targetCell, payload) != ItemDragAcceptance.Accept)
|
||||
InventoryContainerPlacementRejection legality = EvaluateDrop(payload.ObjId);
|
||||
if (legality != InventoryContainerPlacementRejection.None)
|
||||
{
|
||||
if (InventoryContainerPlacementPolicy.ComposeClientLocal(
|
||||
legality,
|
||||
_objects.Get(payload.ObjId),
|
||||
_objects.Get(_openContainer),
|
||||
playerId: 0u) is { } refusal)
|
||||
{
|
||||
_itemInteraction.ReportClientLocal(refusal);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_itemInteraction.EnsureInventoryRequestReady())
|
||||
return;
|
||||
if (_objects.Get(payload.ObjId) is not { } item)
|
||||
|
|
@ -246,17 +262,30 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
InventoryRequestKind kind = amount < fullStack
|
||||
? InventoryRequestKind.SplitToContainer
|
||||
: InventoryRequestKind.PutInContainer;
|
||||
_itemInteraction.TryDispatchInventoryRequest(
|
||||
kind,
|
||||
item.ObjectId,
|
||||
() =>
|
||||
{
|
||||
if (amount < fullStack)
|
||||
if (amount < fullStack)
|
||||
{
|
||||
_itemInteraction.TryDispatchInventoryRequest(
|
||||
kind,
|
||||
item.ObjectId,
|
||||
() =>
|
||||
{
|
||||
_sendSplitToContainer(item.ObjectId, _openContainer, (uint)placement, amount);
|
||||
else
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_itemInteraction.TryDispatchPendingBackpackPlacement(
|
||||
item.ObjectId,
|
||||
_openContainer,
|
||||
placement,
|
||||
kind,
|
||||
() =>
|
||||
{
|
||||
_sendPutItemInContainer(item.ObjectId, _openContainer, placement);
|
||||
return true;
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExternalContainerChanged(ExternalContainerTransition transition)
|
||||
|
|
@ -314,10 +343,29 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
AddContainerCell(guid);
|
||||
}
|
||||
|
||||
var visibleContents = new List<uint>();
|
||||
foreach (uint guid in _objects.GetContents(_openContainer))
|
||||
{
|
||||
if (!IsContainer(_objects.Get(guid)))
|
||||
AddContentsCell(guid);
|
||||
visibleContents.Add(guid);
|
||||
}
|
||||
if (_pendingPlacement is { } pending
|
||||
&& pending.ContainerId == _openContainer
|
||||
&& _objects.Get(pending.ItemId) is { } pendingItem
|
||||
&& !IsContainer(pendingItem))
|
||||
{
|
||||
visibleContents.Remove(pending.ItemId);
|
||||
visibleContents.Insert(
|
||||
Math.Clamp(pending.Placement, 0, visibleContents.Count),
|
||||
pending.ItemId);
|
||||
}
|
||||
foreach (uint guid in visibleContents)
|
||||
{
|
||||
bool waiting = _itemInteraction.IsPendingInventorySource(guid)
|
||||
|| _pendingPlacement is { } projection
|
||||
&& projection.ContainerId == _openContainer
|
||||
&& projection.ItemId == guid;
|
||||
AddContentsCell(guid, waiting);
|
||||
}
|
||||
ApplyIndicators();
|
||||
}
|
||||
|
|
@ -334,15 +382,15 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
private void AddContainerCell(uint guid)
|
||||
{
|
||||
UiItemSlot cell = CreateCell(_containerList, guid, ItemDragSource.Ground);
|
||||
cell.Clicked = () => OpenNestedContainer(guid);
|
||||
SetCapacity(cell, guid);
|
||||
_containerList.AddItem(cell);
|
||||
}
|
||||
|
||||
private void AddContentsCell(uint guid)
|
||||
private void AddContentsCell(uint guid, bool waiting = false)
|
||||
{
|
||||
UiItemSlot cell = CreateCell(_contentsList, guid, ItemDragSource.Ground);
|
||||
cell.DoubleClicked = () => _itemInteraction.ActivateItem(guid);
|
||||
cell.SetWaitingState(waiting);
|
||||
cell.DragAcceptSprite = 0x060011F9u;
|
||||
cell.DragRejectSprite = 0x060011F8u;
|
||||
_contentsList.AddItem(cell);
|
||||
|
|
@ -387,7 +435,10 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
{
|
||||
if (_itemInteraction.OfferPrimaryClick(guid) != ItemPrimaryClickResult.NotActive)
|
||||
return true;
|
||||
Select(guid);
|
||||
if (IsContainer(_objects.Get(guid)) && guid != _state.CurrentContainerId)
|
||||
OpenNestedContainer(guid);
|
||||
else
|
||||
Select(guid);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -406,7 +457,8 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
bool pendingSource = _itemInteraction.IsPendingSource(cell.ItemId);
|
||||
cell.Selected = cell.ItemId != 0u
|
||||
&& cell.ItemId == _selection.SelectedObjectId
|
||||
&& !pendingSource;
|
||||
&& !pendingSource
|
||||
&& !_itemInteraction.IsPendingInventorySource(cell.ItemId);
|
||||
cell.IsOpenContainer = cell.ItemId != 0u && cell.ItemId == _openContainer;
|
||||
}
|
||||
}
|
||||
|
|
@ -486,7 +538,48 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
}
|
||||
|
||||
private void OnSelectionChanged(SelectionTransition _) => ApplyIndicators();
|
||||
private void OnInteractionStateChanged() => ApplyIndicators();
|
||||
private void OnInteractionStateChanged()
|
||||
{
|
||||
if (_window.IsVisible)
|
||||
Populate();
|
||||
else
|
||||
ApplyIndicators();
|
||||
}
|
||||
|
||||
private void OnPendingPlacementRequested(PendingBackpackPlacement pending)
|
||||
{
|
||||
if (pending.ContainerId != _openContainer)
|
||||
return;
|
||||
_pendingPlacement = pending;
|
||||
if (_window.IsVisible)
|
||||
Populate();
|
||||
}
|
||||
|
||||
private void OnPendingPlacementCancelled(PendingBackpackPlacement pending)
|
||||
=> ResolvePendingPlacement(pending);
|
||||
|
||||
private void OnPendingPlacementResolved(PendingBackpackPlacement pending)
|
||||
=> ResolvePendingPlacement(pending);
|
||||
|
||||
private void ResolvePendingPlacement(PendingBackpackPlacement pending)
|
||||
{
|
||||
if (_pendingPlacement is not { } current || current.Token != pending.Token)
|
||||
return;
|
||||
_pendingPlacement = null;
|
||||
if (_window.IsVisible)
|
||||
Populate();
|
||||
}
|
||||
|
||||
private InventoryContainerPlacementRejection EvaluateDrop(uint itemId)
|
||||
{
|
||||
if (_objects.Get(itemId) is { } source && IsContainer(source))
|
||||
return InventoryContainerPlacementRejection.ContainerCapacityFull;
|
||||
return InventoryContainerPlacementPolicy.Evaluate(
|
||||
_objects,
|
||||
itemId,
|
||||
_openContainer,
|
||||
playerId: 0u);
|
||||
}
|
||||
|
||||
private static bool IsContainer(ClientObject? item)
|
||||
=> item is not null
|
||||
|
|
@ -573,6 +666,9 @@ public sealed class ExternalContainerController : IItemListDragHandler, IRetaine
|
|||
_objects.Cleared -= OnObjectsCleared;
|
||||
_selection.Changed -= OnSelectionChanged;
|
||||
_itemInteraction.StateChanged -= OnInteractionStateChanged;
|
||||
_itemInteraction.PendingBackpackPlacementRequested -= OnPendingPlacementRequested;
|
||||
_itemInteraction.PendingBackpackPlacementCancelled -= OnPendingPlacementCancelled;
|
||||
_itemInteraction.PendingBackpackPlacementResolved -= OnPendingPlacementResolved;
|
||||
_topContainer.PrimaryItemPressed = null;
|
||||
_containerList.PrimaryItemPressed = null;
|
||||
_contentsList.PrimaryItemPressed = null;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue