Skip to content

Articles

How to make a shop in Godot

Sell from an Item Vault inventory with DropTable stock, atomic buys, and game-owned prices: the vault owns counts, your game owns the gold.

This recipe builds a shop that sells from an Item Vault inventory: stocked shelves, prices, atomic buys, and save-safe stock. The split is the whole recipe. Item Vault owns every count (definitions, stock, atomic mutation); your game owns every meaning (prices, gold, who may buy). A sale applies gold and goods only after the vault commits — never before.

1. Stock the shelves with a drop table

A shop shelf is a DropTable: each DropEntry points at an ItemDefinition with a quantity and odds, and the table rolls opening stock. The definition owns the stable id and world scene; the table only decides what can appear and how many units show up:

var sword_drop := DropEntry.new()
sword_drop.item = sword_definition
sword_drop.quantity = 2
sword_drop.odds = 1.0 # always stocked; use lower odds for rare stock

var shelf := DropTable.new()
shelf.possible_drops = [sword_drop, potion_drop]
shelf.guaranteed_drops = 1
shelf.combined_odds = false # each entry rolls independently

combined_odds = false rolls each entry on its own; true weights entries against each other and picks one. Restocking is just re-rolling the same table. Full roll behavior — including DropsGenerator for deciding drops before spawning anything — is in the Drop Tables guide.

2. Price in the game, never in the vault

Prices, discounts, and reputation effects live in your game rules, next to the gold purse. Item Vault never sees a price: it moves stacks, your code moves gold, and the two meet only after a committed mutation. That separation is what keeps a failed sale from eating the player's money.

3. Sell on commit, not on click

Follow the consequence-follows-commit rule: game rules first, atomic vault mutation second, gold and effects last. A rejected mutation — no stock, no gold, no capacity for the bought stack — must leave both inventories and the purse untouched:

# 1) Game rules FIRST — a rejection never touches inventory.
if player_gold < price:
    return {"committed": false, "reason": "not enough gold"}
# 2) Atomic vault mutation (remove from shelf, add to player).
var result := _move_stock(shelf_inventory, player_inventory, item_id, 1)
if not result.committed:
    return result
# 3) Consequence ONLY after commit.
player_gold -= price
_play_cash_sound()

This is the ItemUseAuthority pattern — gate, transact, then consequence — documented with a runnable example in Game-Owned Gameplay Integration. Buy, sell-back, and repair are all the same shape: the direction of the stack move changes, the commit ordering never does.