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

@ -99,6 +99,9 @@ internal sealed class SelectionInteractionController
{
ArgumentNullException.ThrowIfNull(payload);
uint target = _query.PickAt(mouseX, mouseY, includeSelf: true) ?? 0u;
if (ItemInteractionController.PhysicsDiagnosticsTradeProbe)
Console.WriteLine(
$"[trade] drag-release item=0x{payload.ObjId:X8} pick=0x{target:X8}");
if (target != 0u)
_query.BeginLightingPulse(target);
_items.PlaceIn3D(payload, target);
@ -278,6 +281,17 @@ internal sealed class SelectionInteractionController
{
CancelPendingApproach();
// Gate fix (2026-08-14 trade round 1): retail's use dispatch
// (CPlayerSystem::UsingItem @ 0x00562F70 → DetermineUseResult
// @ 0x00588460 result 5) routes "Use on another PLAYER" to secure
// trade and never sends 0x0036 for it — this path previously
// dispatched a plain Use the server ignores.
if (_items.TryOpenSecureTradeWithPlayer(serverGuid))
{
reservation?.CancelBeforeDispatch();
return;
}
bool ownedByPlayer = _items.IsOwnedByPlayer(serverGuid);
bool useable = ownedByPlayer || _query.IsUseable(serverGuid);