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 independentlycombined_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.