fix: trade gate round 1 - the Use-on-player route, authored grid cells,

and [trade] seam probes

- Use on a selected player did nothing: the world Use path
  (SelectionInteractionController.RequestUse) dispatches 0x0036 directly
  and NEVER consults DetermineUseResult - lane C's "both paths already
  classified" claim described the POLICY's capability, not a live
  caller (the verify-subagent-claims lesson, again). Retail's
  CPlayerSystem::UsingItem @0x00562F70 consults it and routes result 5
  (another PLAYER) to AttemptToOpenTradeNegotiations @0x0056DEE0, peace
  mode only, with no Use send. Ported as
  ItemInteractionController.TryOpenSecureTradeWithPlayer, called at
  RequestUse entry.
- The broken window: both grids rendered their raw authored strip art
  with no cell layout (the gold-ring tile was the naked track). They now
  get the vendor strips' exact single-row 32px config + the authored
  empty-slot art resolved through ItemListCellTemplate (cell-template
  attr 0x1000000E -> 0x1000033A).
- Drag-onto-player "nothing happens": the policy chain reads correct
  end-to-end in code (pick -> PlaceIn3D -> StartSecureTrade -> event ->
  cmd), so TEMPORARY [trade] probe lines now bracket every seam
  (drag-release pick, policy arm, controller request, use-on-player) -
  the next gate log pinpoints the break if it persists. Stripped once
  the two-client gate passes.

App suite 4,990/3 skips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-14 12:03:22 +02:00
parent 067cbea8a5
commit 22f30f291d
4 changed files with 103 additions and 2 deletions

View file

@ -58,7 +58,12 @@ public sealed class SecureTradeUiController : IRetainedPanelController
Action<bool /*selfAccepted*/, bool /*partnerAccepted*/, uint /*partner*/> AcceptTrade,
Action DeclineTrade,
Action ResetTrade,
Action<bool> SetWindowVisible);
Action<bool> SetWindowVisible,
// The authored empty-slot background for each grid, resolved via
// ItemListCellTemplate from the lists' own 0x1000000E cell-template
// attribute (0x1000033A) — same recipe as the vendor strips.
uint SelfEmptySlotSprite = 0u,
uint PartnerEmptySlotSprite = 0u);
private readonly Bindings _bindings;
private readonly UiText? _partnerName;
@ -122,9 +127,34 @@ public sealed class SecureTradeUiController : IRetainedPanelController
// dropped on the grid stages it.
_selfList?.RegisterDragHandler(new SelfGridDropHandler(this));
// Gate fix (2026-08-14 round 1: "broken trade window"): the grids
// rendered their raw authored strip art with no cell layout at all —
// the same single-row 32px config + authored empty-slot fill the
// vendor/external-container strips use.
ConfigureGrid(_selfList, bindings.SelfEmptySlotSprite);
ConfigureGrid(_partnerList, bindings.PartnerEmptySlotSprite);
_bindings.SetWindowVisible(false);
}
private static void ConfigureGrid(UiItemList? list, uint emptySlotSprite)
{
if (list is null) return;
list.Columns = 1;
list.SingleRow = true;
list.HorizontalScroll = true;
list.CellWidth = 32f;
list.CellHeight = 32f;
list.FillVisibleEmptySlots = true;
if (emptySlotSprite != 0u)
list.CellEmptySprite = emptySlotSprite;
list.EmptySlotFactory = () => new UiItemSlot
{
SpriteResolve = list.SpriteResolve,
AllowDragSource = false,
};
}
public static SecureTradeUiController? Bind(
ImportedLayout layout, Bindings bindings)
{
@ -148,6 +178,10 @@ public sealed class SecureTradeUiController : IRetainedPanelController
public void RequestSecureTrade(uint partnerGuid, uint itemGuid)
{
if (_disposed || partnerGuid == 0u) return;
if (ItemInteractionController.PhysicsDiagnosticsTradeProbe)
Console.WriteLine(
$"[trade] request partner=0x{partnerGuid:X8} item=0x{itemGuid:X8} "
+ $"open={_bindings.Trade.Snapshot.IsOpen}");
RuntimeTradeSnapshot snapshot = _bindings.Trade.Snapshot;
if (snapshot.IsOpen && snapshot.PartnerGuid == partnerGuid)
{