Skip to content

fix!(lua.endpoints): allow buy/pack of Negative-edition cards when slots are full #208

Description

@S1M0N38

The buy and pack endpoints reject valid purchases of Negative-edition jokers/consumables when inventory is full. The game allows them because a Negative card brings its own slot. Verified against vendors/lovely/dump/ and confirmed empirically.

Bug

Both endpoints use a plain count >= limit check:

  • src/lua/endpoints/buy.lua:134 (jokers), buy.lua:149 (consumables)
  • src/lua/endpoints/pack.lua:142 (jokers)

The game uses a more permissive formula that accounts for the card's own slot capacity. Live source:

  • check_for_buy_space (functions/button_callbacks.lua:2427): #G.jokers.cards < G.jokers.config.card_limit + card.ability.card_limit - card.ability.extra_slots_used
  • can_select_card (functions/button_callbacks.lua:2114, SMODS-patched in smods/lovely/booster.toml:333 "Support negative-ish on Jokers"): same card_limit math

Empirical: added a Negative joker to a full inventory (5/5) → count went 6/6 (limit +1). The Negative edition grants card.ability.card_limit = 1, so the game allows the purchase/selection; balatrobot blocks it with "joker slots are full".

Fix

Replicate the game formula. For jokers:

-- instead of: if count >= limit
if #G.jokers.cards >= G.jokers.config.card_limit + (card.ability.card_limit or 0) - (card.ability.extra_slots_used or 0)

Same for consumables (G.consumeables). Read the shop/pack card's ability.card_limit/extra_slots_used from the live card object (G.shop_jokers.cards[pos] / G.pack_cards.cards[pos]), not the formatted gamestate.

PoC test

def test_buy_negative_joker_when_full(self, client):
    """Negative joker can be bought even when joker slots are full."""
    gamestate = load_fixture(client, "buy",
        "state-SHOP--jokers.count-5--shop.cards[0].key-j_joker--shop.cards[0].edition-e_negative")
    assert gamestate["jokers"]["count"] == 5
    assert gamestate["jokers"]["limit"] == 5
    assert gamestate["shop"]["cards"][0]["modifier"]["edition"] == "e_negative"
    # Game allows: negative brings +1 slot. balatrobot currently blocks.
    response = api(client, "buy", {"card": 0})
    after = assert_gamestate_response(response)
    assert after["jokers"]["count"] == 6
    assert after["jokers"]["limit"] == 6

Fixture note: placing a Negative joker in the shop requires either a seed hunt or extending add to place jokers/consumables into G.shop_jokers/G.consumeables (it currently only adds packs/vouchers to shop areas).

Sibling of #202#206.

Metadata

Metadata

Assignees

Labels

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

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions