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
|
|
@ -117,21 +117,28 @@ public sealed class VendorShopItemMaterializerTests
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// G2 (grand-gate finding): a REAL ACE vendor listing carries
|
||||
/// R1 gate-finding fix (2026-08-08, register AP-169 correction,
|
||||
/// replacing the retired <c>Apply_NoDescStackSize_FallsBackToPackedSupplyCount</c>
|
||||
/// G2 test below). A REAL ACE vendor listing for an UNLIMITED-STOCK
|
||||
/// stackable item (e.g. a Prismatic Taper) carries
|
||||
/// <c>DescStackSize=null</c> (ACE's <c>Vendor.LoadInventoryItem</c> never
|
||||
/// calls <c>wo.SetStackSize</c> on the browse-list WorldObject) and only
|
||||
/// the packed <see cref="VendorShopItem.StackSize"/> supply-count field
|
||||
/// (<c>VendorShopCreateListStackSize</c>) names a real quantity. Before
|
||||
/// the fix, <c>ToWeenieData</c> read only <c>DescStackSize</c>, so
|
||||
/// <see cref="ClientObject.StackSize"/> came back 1 for every vendor
|
||||
/// item — the toolbar split slider (which gates on
|
||||
/// <c>stackSize > 1</c>) never appeared for ANY vendor stack. This
|
||||
/// pins the fallback: no <c>DescStackSize</c>, packed
|
||||
/// <c>StackSize=100</c> -> <c>ClientObject.StackSize</c> resolves to
|
||||
/// 100, not 1.
|
||||
/// calls <c>wo.SetStackSize</c> on the browse-list WorldObject) AND the
|
||||
/// packed <see cref="VendorShopItem.StackSize"/> supply-count field at
|
||||
/// its UNLIMITED sentinel (<c>-1</c>) — so neither wire "how many"
|
||||
/// signal names a usable ceiling. The G2 fix's packed-supply-count
|
||||
/// fallback (the ORIGINAL version of this test) produced nothing usable
|
||||
/// for exactly this case — a live re-test confirmed the toolbar split
|
||||
/// bar stayed hidden, matching the live retail screenshot report (see
|
||||
/// AP-169's corrected story). This pins the CORRECT fallback:
|
||||
/// <see cref="VendorShopItem.MaxStackSize"/> — the item TYPE's authored
|
||||
/// stack ceiling, which ACE DOES reliably populate (an ordinary weenie
|
||||
/// property, not an instance-specific "how many for sale" count) — no
|
||||
/// <c>DescStackSize</c>, packed <c>StackSize=-1</c> (unlimited),
|
||||
/// <c>MaxStackSize=1000</c> -> <c>ClientObject.StackSize</c> resolves
|
||||
/// to 1000, not 1 and not the (nonsensical, unbounded) packed field.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Apply_NoDescStackSize_FallsBackToPackedSupplyCount()
|
||||
public void Apply_UnlimitedStockNoDescStackSize_FallsBackToMaxStackSize()
|
||||
{
|
||||
var vendor = new VendorState();
|
||||
var objects = new ClientObjectTable();
|
||||
|
|
@ -140,21 +147,27 @@ public sealed class VendorShopItemMaterializerTests
|
|||
vendor.Apply(VendorGuid, default, new[]
|
||||
{
|
||||
new VendorShopItem(
|
||||
ItemA, StackSize: 100, WeenieClassId: 1u, Name: "Arrow",
|
||||
ItemType: (uint)ItemType.MissileWeapon, IconId: 0x1234u, Value: 100,
|
||||
DescStackSize: null),
|
||||
ItemA, StackSize: -1, WeenieClassId: 1u, Name: "Prismatic Taper",
|
||||
ItemType: (uint)ItemType.SpellComponents, IconId: 0x1234u, Value: 100,
|
||||
DescStackSize: null, MaxStackSize: 1000),
|
||||
});
|
||||
|
||||
Assert.Equal(100, objects.Get(ItemA)!.StackSize);
|
||||
ClientObject item = objects.Get(ItemA)!;
|
||||
Assert.Equal(1000, item.StackSize);
|
||||
Assert.Equal(1000, item.StackSizeMax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// G2 companion: the packed field's -1 "unlimited supply" sentinel has
|
||||
/// no bounded per-row purchase cap in the wire shape today, so it must
|
||||
/// fall through to the safe non-splittable default (1) rather than
|
||||
/// literally propagating -1 (which would read as a huge unsigned
|
||||
/// "stack size" to <see cref="SelectedObjectController"/>'s
|
||||
/// <c>stackSize > 1</c> gate).
|
||||
/// Sabotage-adjacent control: the SAME unlimited-stock listing but with
|
||||
/// <see cref="VendorShopItem.MaxStackSize"/> ALSO absent (neither wire
|
||||
/// "how many" signal usable at all) still degrades to the safe
|
||||
/// non-splittable default (1) rather than literally propagating the
|
||||
/// packed field's <c>-1</c> sentinel (which would read as a huge
|
||||
/// unsigned "stack size" to <c>SelectedObjectController</c>'s
|
||||
/// <c>stackSize > 1</c> gate). Proves
|
||||
/// <see cref="Apply_UnlimitedStockNoDescStackSize_FallsBackToMaxStackSize"/>'s
|
||||
/// 1000 result comes from the MaxStackSize field specifically, not from
|
||||
/// some other code path that would resolve to 1000 regardless.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Apply_UnlimitedSupplySentinel_FallsBackToNonSplittableDefault()
|
||||
|
|
@ -163,7 +176,7 @@ public sealed class VendorShopItemMaterializerTests
|
|||
var objects = new ClientObjectTable();
|
||||
using var materializer = new VendorShopItemMaterializer(vendor, objects);
|
||||
|
||||
vendor.Apply(VendorGuid, default, new[] { Item(ItemA, "Bread") }); // StackSize: -1, DescStackSize: null
|
||||
vendor.Apply(VendorGuid, default, new[] { Item(ItemA, "Bread") }); // StackSize: -1, DescStackSize: null, MaxStackSize: null
|
||||
|
||||
Assert.Equal(1, objects.Get(ItemA)!.StackSize);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue