feat!(lua.endpoints): support buy-and-use for shop consumables
- Dominant language
- Python
- Stars
- 72
- Forks
- 18
- PR merge metrics
- No merged PRs in 30d
Description
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
```python
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.
Contributor guide
Assessment
This issue has not been assessed yet.