The buy endpoint cannot perform the game's "Buy and Use" action for shop consumables — buying and immediately using a consumable without needing a free consumable slot. Verified against vendors/lovely/dump/ and confirmed empirically.
Bug
src/lua/endpoints/buy.lua has no use parameter. When consumable slots are full, buy {"card": N} always fails the check_for_buy_space gate (buy.lua:149) with "consumable slots are full".
The game exposes a separate "Buy and Use" button for shop consumables. buy_from_shop (functions/button_callbacks.lua:2437) skips check_for_buy_space when e.config.id == 'buy_and_use', then calls G.FUNCS.use_card(e, true) (button_callbacks.lua:2503). This buys the consumable and uses it in one step, so no slot is ever occupied. Gated by can_buy_and_use (button_callbacks.lua:79).
Empirical: consumables full (2/2), shop has c_venus → buy {"card": 1} returns "consumable slots are full". The in-game "Buy and Use" button would succeed (Venus needs no targets).
The current workaround (buy → use) is not equivalent: it requires a free slot to hold the consumable between the two calls, which buy_and_use avoids entirely.
Fix
Add an optional use boolean to the buy schema. When set with a consumable card, set the mock button's config.id = 'buy_and_use' before calling buy_from_shop, and pass through any target cards to the underlying use_card. Mirror the use endpoint's target-validation logic for consumables that require hand selection.
PoC test
def test_buy_and_use_consumable_when_full(self, client):
"""Buy+use a shop consumable even when consumable slots are full."""
gamestate = load_fixture(client, "buy",
"state-SHOP--consumables.count-2--shop.cards[0].key-c_venus")
assert gamestate["consumables"]["count"] == 2
assert gamestate["consumables"]["limit"] == 2
assert gamestate["shop"]["cards"][0]["key"] == "c_venus"
# Game's buy_and_use skips the slot check. balatrobot currently blocks.
response = api(client, "buy", {"card": 0, "use": True})
after = assert_gamestate_response(response)
# Venus levels up a hand; consumable count unchanged (used, not stored)
assert after["consumables"]["count"] == 2
Fixture note: needs a no-target consumable (e.g. c_venus, c_pluto) in the shop. Same add-to-shop fixture gap as the Negative-edition issue.
Sibling of #202–#206.
The
buyendpoint cannot perform the game's "Buy and Use" action for shop consumables — buying and immediately using a consumable without needing a free consumable slot. Verified againstvendors/lovely/dump/and confirmed empirically.Bug
src/lua/endpoints/buy.luahas nouseparameter. When consumable slots are full,buy {"card": N}always fails thecheck_for_buy_spacegate (buy.lua:149) with "consumable slots are full".The game exposes a separate "Buy and Use" button for shop consumables.
buy_from_shop(functions/button_callbacks.lua:2437) skipscheck_for_buy_spacewhene.config.id == 'buy_and_use', then callsG.FUNCS.use_card(e, true)(button_callbacks.lua:2503). This buys the consumable and uses it in one step, so no slot is ever occupied. Gated bycan_buy_and_use(button_callbacks.lua:79).Empirical: consumables full (2/2), shop has
c_venus→buy {"card": 1}returns "consumable slots are full". The in-game "Buy and Use" button would succeed (Venus needs no targets).The current workaround (buy → use) is not equivalent: it requires a free slot to hold the consumable between the two calls, which buy_and_use avoids entirely.
Fix
Add an optional
useboolean to thebuyschema. When set with a consumablecard, set the mock button'sconfig.id = 'buy_and_use'before callingbuy_from_shop, and pass through any targetcardsto the underlyinguse_card. Mirror theuseendpoint's target-validation logic for consumables that require hand selection.PoC test
Fixture note: needs a no-target consumable (e.g.
c_venus,c_pluto) in the shop. Sameadd-to-shop fixture gap as the Negative-edition issue.Sibling of #202–#206.