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 moves goods, your game moves gold after commit.

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. Keep the shelf and the player's bag as two containers on one Inventory: InventoryTransaction spans containers of a single inventory, so one execute() moves the unit on both sides or leaves both untouched — there is no cross-inventory transaction. A rejected mutation — no stock, no gold, no capacity for the bought stack — must leave both containers and the purse exactly as they were:

# 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 the shelf, add to the player.
var tx := inventory.create_transaction()
tx.remove_item(item_id, 1, &"shelf")
tx.add_item(item_id, 1, &"player")
if not tx.execute(): # bool; false means the removal rolled back too
    return {"committed": false, "reason": "vault rejected"}
# 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.