Skip to content

Commit d40d645

Browse files
committed
fix: allow to sell jokers when a Buffoon pack is open
Closes #156.
1 parent e009e82 commit d40d645

4 files changed

Lines changed: 57 additions & 5 deletions

File tree

docs/api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ curl -X POST http://127.0.0.1:12346 \
395395

396396
### `sell`
397397

398-
Sell a joker or consumable.
398+
Sell a joker or consumable. Available in SHOP, SELECTING_HAND states, and when a Buffoon pack is open (to make room for new jokers).
399399

400400
**Parameters:** (exactly one required)
401401

@@ -406,7 +406,7 @@ Sell a joker or consumable.
406406

407407
**Returns:** [GameState](#gamestate-schema)
408408

409-
**Errors:** `BAD_REQUEST`, `NOT_ALLOWED`
409+
**Errors:** `BAD_REQUEST`, `INVALID_STATE`, `NOT_ALLOWED`
410410

411411
**Example:**
412412

src/lua/endpoints/sell.lua

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ return {
3232
},
3333
},
3434

35-
requires_state = { G.STATES.SELECTING_HAND, G.STATES.SHOP },
35+
requires_state = { G.STATES.SELECTING_HAND, G.STATES.SHOP, G.STATES.SMODS_BOOSTER_OPENED },
3636

3737
---@param args Request.Endpoint.Sell.Params
3838
---@param send_response fun(response: Response.Endpoint)
@@ -55,6 +55,22 @@ return {
5555
return
5656
end
5757

58+
-- If in SMODS_BOOSTER_OPENED, verify it's a Buffoon pack (contains Jokers)
59+
if G.STATE == G.STATES.SMODS_BOOSTER_OPENED then
60+
local pack_set = G.pack_cards
61+
and G.pack_cards.cards
62+
and G.pack_cards.cards[1]
63+
and G.pack_cards.cards[1].ability
64+
and G.pack_cards.cards[1].ability.set
65+
if pack_set ~= "Joker" then
66+
send_response({
67+
message = "Can only sell jokers when a Buffoon pack is open",
68+
name = BB_ERROR_NAMES.NOT_ALLOWED,
69+
})
70+
return
71+
end
72+
end
73+
5874
-- Determine which type to sell and validate existence
5975
local source_array, pos, sell_type
6076

@@ -144,7 +160,11 @@ return {
144160
local state_stable = G.STATE_COMPLETE == true
145161

146162
-- 5. Still in valid state
147-
local valid_state = (G.STATE == G.STATES.SHOP or G.STATE == G.STATES.SELECTING_HAND)
163+
local valid_state = (
164+
G.STATE == G.STATES.SHOP
165+
or G.STATE == G.STATES.SELECTING_HAND
166+
or G.STATE == G.STATES.SMODS_BOOSTER_OPENED
167+
)
148168

149169
-- All conditions must be met
150170
if count_decreased and money_increased and card_gone and state_stable and valid_state then

src/lua/utils/openrpc.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@
577577
{
578578
"name": "sell",
579579
"summary": "Sell a joker or consumable",
580-
"description": "Sell a joker or consumable from player inventory. Must provide exactly one of: joker or consumable.",
580+
"description": "Sell a joker or consumable from player inventory. Must provide exactly one of: joker or consumable. Available in SHOP, SELECTING_HAND states, and when a Buffoon pack is open (SMODS_BOOSTER_OPENED state with Joker set pack) to make room for new jokers.",
581581
"tags": [
582582
{
583583
"$ref": "#/components/tags/shop"
@@ -614,6 +614,9 @@
614614
{
615615
"$ref": "#/components/errors/BadRequest"
616616
},
617+
{
618+
"$ref": "#/components/errors/InvalidState"
619+
},
617620
{
618621
"$ref": "#/components/errors/NotAllowed"
619622
}

tests/lua/endpoints/test_pack.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,35 @@ def test_pack_joker_slots_full(self, client: httpx.Client) -> None:
158158
"Cannot select joker, joker slots are full. Current: 5, Limit: 5",
159159
)
160160

161+
def test_pack_joker_slots_full_sell_joker(self, client: httpx.Client) -> None:
162+
"""Test selling a joker to make room when joker slots are full during pack selection."""
163+
gamestate = load_fixture(
164+
client,
165+
"pack",
166+
"state-SMODS_BOOSTER_OPENED--pack.type-buffoon--jokers.count-5",
167+
)
168+
assert gamestate["jokers"]["count"] == 5
169+
before_jokers = set(j["key"] for j in gamestate["jokers"]["cards"])
170+
result = api(client, "sell", {"joker": 0})
171+
gamestate = assert_gamestate_response(result)
172+
assert gamestate["jokers"]["count"] == 4
173+
result = api(client, "pack", {"card": 0})
174+
gamestate = assert_gamestate_response(result, state="SHOP")
175+
assert gamestate["jokers"]["count"] == 5
176+
after_jokers = set(j["key"] for j in gamestate["jokers"]["cards"])
177+
assert before_jokers != after_jokers
178+
179+
def test_pack_tarot_try_to_sell_joker(self, client: httpx.Client) -> None:
180+
"""Test that selling jokers is not allowed when a non-buffoon pack is open."""
181+
load_fixture(
182+
client, "pack", "state-SMODS_BOOSTER_OPENED--pack.cards[0].key-c_heirophant"
183+
)
184+
assert_error_response(
185+
api(client, "sell", {"joker": 0}),
186+
"NOT_ALLOWED",
187+
"Can only sell jokers when a Buffoon pack is open",
188+
)
189+
161190
def test_pack_joker_slots_available(self, client: httpx.Client) -> None:
162191
"""Test selecting joker when slots available succeeds."""
163192
load_fixture(

0 commit comments

Comments
 (0)