fix(vendor): re-gate residuals — MaxStackSize is the stack operand, wire-authored use radius, purse summaries
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
Some checks are pending
Headless portability / portable-headless (ubuntu-latest) (push) Waiting to run
Headless portability / portable-headless (windows-latest) (push) Waiting to run
Headless portability / linux-graphical (push) Waiting to run
Headless portability / linux-vulkan (push) Waiting to run
R1 the split bar's operand is the item's authored MaxStackSize —
three retail sites read pwd._maxStackSize directly (InqListSlotCount
pc:200052, buy-button cases pc:203996/204086) where ACE never fills
the desc stack and standard stock is unlimited. Threaded StackSizeMax
end to end with one shared resolver; the two literal _maxStackSize
sites are now byte-exact; AP-165 retired, AP-169 corrected.
R2 walk-to-vendor never opened because GetUseRadius used an UNCITED
3m Creature heuristic as the local stop distance while ACE's poll
demands the authored radius (default 0.6 m) — the walk stopped and
the Use fired far outside acceptance. Now reads the wire-authored
spawn UseRadius with ACE's exact fallback; heuristic constants
deleted. A first sabotage attempt was non-discriminating
(coincidental 0.6) and was corrected — the discriminating version is
what landed.
R3 the Buying/Selling purse summaries ("Buying %d %s worth %hsp" /
"You have %hsp") recovered from the binary data segment where BN
mis-attributes the Buy-side literal; wired to staging and money
changes on the four authored text elements; AP-166 narrowed to the
pending-sell highlight.
Clean-room complete solution: 11,528 passed / 4 skipped / 0 failed.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
68568a3a59
commit
d003449bb4
11 changed files with 680 additions and 103 deletions
|
|
@ -76,11 +76,13 @@ internal sealed class WorldSelectionQuery
|
|||
: IWorldSelectionQuery,
|
||||
IRetainedUiSelectionQuery
|
||||
{
|
||||
private const uint LargeUseObjectFlags = 0x1000u | 0x4000u | 0x40000u | 0x2000u;
|
||||
private const uint StuckObjectFlag = 0x0004u;
|
||||
/// <summary>
|
||||
/// ACE's own fallback when a target authors no wire <c>UseRadius</c> at
|
||||
/// all (<c>WorldObject_Use.cs:50</c>, <c>useRadius ?? 0.6f</c>) — see
|
||||
/// <see cref="GetUseRadius"/>.
|
||||
/// </summary>
|
||||
private const float DefaultUseRadius = 0.6f;
|
||||
private const float CreatureUseRadius = 3f;
|
||||
private const float LargeObjectUseRadius = 2f;
|
||||
private const float AceCanChargeDistance = 7.5f;
|
||||
|
||||
private const uint SmallItemMask =
|
||||
|
|
@ -527,13 +529,46 @@ internal sealed class WorldSelectionQuery
|
|||
ignoreZDelta: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// R2 gate-finding fix (2026-08-08, grand-gate walk-to-vendor
|
||||
/// regression): retail reads the TARGET's own wire-authored
|
||||
/// <c>PublicWeenieDesc::_useRadius</c> directly for every
|
||||
/// range/approach purpose — <c>CPlayerSystem::RegisterObjectRangeHandler</c>
|
||||
/// (<c>pc:195159</c>/<c>203677</c>/<c>210429</c>; <c>203677</c> is
|
||||
/// <c>gmVendorUI::OpenVendor</c>'s own range-handler registration,
|
||||
/// reading <c>eax->pwd._useRadius</c> for the VENDOR target itself,
|
||||
/// the exact NPC kind this bug was found on). ACE's server-side
|
||||
/// acceptance test (<c>WorldObject_Use.cs:47-55</c>,
|
||||
/// <c>IsWithinUseRadiusOf</c>) reads the SAME wire field:
|
||||
/// <c>useRadius ?? 0.6f</c> — no item-type special-casing at all.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>What this replaces.</b> The prior implementation ignored the wire
|
||||
/// field entirely and guessed a flat radius from the target's item
|
||||
/// type/flags (3m for ANY <see cref="ItemType.Creature"/>, 2m for a
|
||||
/// "large object" flag combination, 0.6m otherwise) — an uncited,
|
||||
/// retail-incorrect heuristic. For a typical vendor NPC (Creature-typed,
|
||||
/// authoring a much tighter UseRadius than 3m in practice) this made
|
||||
/// the CLIENT's own local "arrived" test (<c>MoveToManager</c>'s
|
||||
/// <see cref="AcDream.Core.Physics.Motion.MoveToManager.GetCurrentDistance"/>
|
||||
/// cylinder-distance arrival check, gated on
|
||||
/// <see cref="AcDream.Core.Physics.Motion.MovementParameters.DistanceToObject"/>
|
||||
/// = this method's return value) satisfied well outside ACE's real
|
||||
/// acceptance zone. The player's walk stopped — and the AP-170/G3
|
||||
/// arrival-gated Use dispatched — several meters before the player was
|
||||
/// ever within ACE's own poll-based <c>WithinUseRadius</c> check
|
||||
/// (<c>Player_Move.cs</c>'s <c>CreateMoveToChain</c>), so
|
||||
/// <c>ApproachVendor</c> never arrived: the exact "Use lost silently"
|
||||
/// defect AP-170 closed, reproduced from a different angle (a too-loose
|
||||
/// LOCAL arrival threshold racing ACE's tighter real one, not a missing
|
||||
/// arrival gate). The vendor's cosmetic greeting the user observed on
|
||||
/// approach is a SEPARATE, distance-only proximity emote independent of
|
||||
/// this Use-radius gate — its firing does not imply the player was ever
|
||||
/// within ACE's real UseRadius.
|
||||
/// </remarks>
|
||||
private float GetUseRadius(uint serverGuid)
|
||||
{
|
||||
if ((GetItemType(serverGuid) & ItemType.Creature) != 0)
|
||||
return CreatureUseRadius;
|
||||
return _liveEntities.TryGetSnapshot(serverGuid, out var spawn)
|
||||
&& ((spawn.ObjectDescriptionFlags ?? 0u) & LargeUseObjectFlags) != 0u
|
||||
? LargeObjectUseRadius
|
||||
=> _liveEntities.TryGetSnapshot(serverGuid, out var spawn)
|
||||
&& spawn.UseRadius is > 0f
|
||||
? spawn.UseRadius.Value
|
||||
: DefaultUseRadius;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue