Skip to content

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

Description

@S1M0N38

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_venusbuy {"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.

Metadata

Metadata

Assignees

Labels

completed-in-devThis issue have been solved in dev branch

Fields

No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions