coder / coder/balatrobot

feat!(lua.endpoints): support buy-and-use for shop consumables

Open
#209 1 comment 0 reactions 1 assignee Claimed by @S1M0N38 View on GitHub
completed-in-dev
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.