You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/qa/manual-client-checklist.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -324,6 +324,16 @@ Expected result:
324
324
- selected-character anti-flagged or transfer-guarded equipment fails closed: no item refresh, no quickslot change, no point change, no carried/equipment mutation, and no persistence change
325
325
- equipment whose template-authored `equip_effect` point delta would overflow the bootstrap signed 32-bit point value also fails closed before item, quickslot, point, or persistence mutation
-[ ] Open a known bootstrap merchant window with a disposable QA character
330
+
-[ ] Attempt to buy a catalog item whose authored template requires a higher `min_level` than the selected character has
331
+
-[ ] Attempt to sell a carried item whose authored template requires a higher `min_level` than the selected character has
332
+
333
+
Expected result:
334
+
- both packet paths fail with the current merchant invalid-position companion and no inventory, item quickslot, gold, or persisted account mutation is visible
335
+
- adjacent allowed merchant buy/sell cases still use the template-authored price/sell-credit behavior
Copy file name to clipboardExpand all lines: spec/protocol/npc-shop-transaction-bootstrap.md
+5-4Lines changed: 5 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -134,7 +134,7 @@ When a gated `BUY` request arrives, the runtime must validate all of the followi
134
134
- the entry `count` is greater than zero
135
135
- the selected character has at least that much gold available
136
136
- the selected character has a valid carried-inventory placement for that template/count under `item-stack-bootstrap.md`, including `anti_stack` templates skipping existing-stack merge/fan-out paths
137
-
- the resolved template does not carry a selected-character job/sex restriction (`anti_warrior`, `anti_assassin`, `anti_sura`, `anti_shaman`, `anti_male`, or `anti_female`)
137
+
- the resolved template does not carry a selected-character job/sex/level restriction (`anti_warrior`, `anti_assassin`, `anti_sura`, `anti_shaman`, `anti_male`, `anti_female`, or `min_level` above the selected character's level)
138
138
- persistence/writeback can succeed before the new live state is committed
139
139
140
140
The first buy contract intentionally remains single-entry and immediate:
@@ -190,7 +190,7 @@ The first buy path must fail closed when any of these are true:
190
190
- the catalog/template resolution fails
191
191
- the player has insufficient gold
192
192
- no valid carried inventory placement exists
193
-
- the resolved template carries a selected-character job/sex restriction (`anti_warrior`, `anti_assassin`, `anti_sura`, `anti_shaman`, `anti_male`, or `anti_female`)
193
+
- the resolved template carries a selected-character job/sex/level restriction (`anti_warrior`, `anti_assassin`, `anti_sura`, `anti_shaman`, `anti_male`, `anti_female`, or `min_level` above the selected character's level)
194
194
- persistence/writeback fails
195
195
196
196
The first sell/sell2 path must fail closed when any of these are true:
@@ -202,7 +202,7 @@ The first sell/sell2 path must fail closed when any of these are true:
202
202
- the requested carried cell has duplicate live item occupancy
203
203
- the carried item is marked runtime-locked
204
204
- the template is marked `anti_sell`
205
-
- the resolved template carries a selected-character job/sex restriction (`anti_warrior`, `anti_assassin`, `anti_sura`, `anti_shaman`, `anti_male`, or `anti_female`)
205
+
- the resolved template carries a selected-character job/sex/level restriction (`anti_warrior`, `anti_assassin`, `anti_sura`, `anti_shaman`, `anti_male`, `anti_female`, or `min_level` above the selected character's level)
206
206
- the template has no sell price
207
207
- persistence/writeback fails
208
208
@@ -346,8 +346,9 @@ The first live sell-back contract remains intentionally narrow:
346
346
- ordinary sell credit derives from loaded item-template `shop_buy_price` as `floor((shop_buy_price * sold_count) / 5)` minus `floor(3% tax)`
347
347
- templates flagged `sell_count_per_gold` follow the legacy count-per-gold branch first: use `floor(sold_count / shop_buy_price)` when `shop_buy_price > 0`, or `sold_count` when it is zero, then apply the same `/5` and 3% tax floor; if the resulting credit is zero, the bootstrap runtime fails closed
348
348
- templates flagged `anti_sell` fail closed before credit calculation, return bare self-only `GC::SHOP INVALID_POS` on the packet sell path while a merchant window is active, and leave live plus persisted inventory/currency unchanged
349
-
- the player mutation boundary also exposes a template-backed sell helper that rejects `anti_sell` templates, template/item-`vnum` mismatches, invalid template metadata, invalid counts, locked carried items, equipped carried items, and zero-credit pricing before mutating live inventory or gold
349
+
- the player mutation boundary also exposes a template-backed sell helper that rejects `anti_sell` templates, template/item-`vnum` mismatches, invalid template metadata, invalid counts, locked carried items, equipped carried items, selected-character job/sex/level restrictions, and zero-credit pricing before mutating live inventory or gold
350
350
- successful template-backed sell uses the same template-derived `MerchantSellCredit` calculation as the packet/runtime sell path, then delegates to the existing credit mutation so whole-stack and partial-stack state results stay aligned
351
+
- selected-character `min_level` restrictions are checked through the same template-use guard as job/sex anti-flags; packet `SHOP BUY`, packet `SHOP SELL` / `SELL2`, and the template-backed player-runtime sell helper all fail closed before inventory, quickslot, gold, or persistence mutation when the selected character is below the authored minimum level
351
352
- the updated selected-character snapshot is persisted before the live shared-world registration is refreshed
352
353
- if persistence/writeback fails, the runtime rolls the selected character's live gold and carried inventory back to the pre-sell snapshot, emits no success frames, and leaves the persisted account snapshot unchanged
353
354
- whole-stack success emits self-only `ITEM_DEL(slot)`, then zero or more self-only `QUICKSLOT_DEL(position)` frames for item quickslots that referenced the removed carried slot, then self-only `PLAYER_POINT_CHANGE(type = POINT_GOLD, amount = credited_elk, value = new_gold)`
0 commit comments