# Money and shops (economy.money, trade.shop) The player's balance and the merchants that exchange it for items. Readable offline: `gripforge docs economy`, or the MCP tool `gripforge_docs` (topic `economy`). ## What it is for | Kit | What it adds | | --- | --- | | `economy.money` | the balance as the game variable `economy.money`: earn and spend with an optional debt, a transaction event for every change, mission rewards paid through it, verbs for scripts | | `trade.shop` | merchants as data: offers with price and stock, what a merchant buys, a sell rate, restocking, interaction prompt, buy and sell rules | The rules live in the kits; the counter itself (the screen) is drawn by the interface layer from the events and services. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "economy.money" } gripforge_gamekit_configure { project_id: "gkp_…", id: "economy.money", config: { "startingMoney": 100, "currency": "gold" } } gripforge_gamekit_install { project_id: "gkp_…", id: "trade.shop" } ``` `economy.money` requires only `game.engine`. `trade.shop` requires `economy.money` and `inventory.basic` (see the Inventory guide), so installing a shop brings both. ## Settings and defaults | Kit | Key | Default | Meaning | | --- | --- | --- | --- | | `economy.money` | `startingMoney` | 0 | balance at the start, when the project does not seed one | | `economy.money` | `currency` | `$` | symbol shown by the HUD (`economy.currency`) | | `economy.money` | `allowDebt` | `false` | let a spend go below zero | | `trade.shop` | `buyRate` | 0.5 | share of an item's price paid when the player sells it | | `trade.shop` | `buysAnything` | `true` | a merchant buys any item that has a price in any shop | | `trade.shop` | `restockMinutes` | 0 | minutes before a sold-out offer comes back (0: never) | | `trade.shop` | `interactLabel` | `Trade with {name}` | the prompt on a merchant; `{name}` is the shop name | A project may also seed the balance in its variables (`{ "economy.money": 250 }` wins over `startingMoney`); a `set_var` on it is simply the new balance. ## Money rules - `add(amount, reason)` credits a finite amount of 0 or more; 0 or an invalid amount changes nothing and emits nothing. - `spend(amount, reason)` debits when the balance covers it, or always with `allowDebt`; otherwise it answers `false` and the balance is untouched. Amounts are rounded to cents. - Mission `rewards.money` is paid through `add(money, "mission:")`, once, just before `mission.completed`. - Service `economy.money`: `balance()`, `currency()`, `canAfford(amount)`, `add(amount, reason?)` (the new balance), `spend(amount, reason?)`. Verbs `add_money`, `spend_money`, condition `money_at_least`; in a script: `{ "add_money": 50 }`. - Events: `economy.transaction { amount, balance, reason }` (signed: plus is a credit), `economy.refused` (a spend that could not be paid). Variables: `economy.money`, `economy.currency`. The balance is part of the saved game state. ## Shops ```json { "shops": [{ "id": "blacksmith", "name": "Blacksmith", "greeting": "Steel for the road?", "offers": [{ "item": "iron_helmet", "price": 120, "stock": 1 }, { "item": "arrow", "price": 2 }] }] } ``` An offer with no `stock` is unlimited. A shop may also list the items the merchant buys (an id, or `{ item, price }` with its own base price) and its own sell rate. A merchant in the world is an entity with the component `shop: { id }`: interacting opens the shop; `open_shop` does the same from a script. - `buy(shop, item, n)` checks the stock and the balance, takes the money and gives the units; a full inventory refuses with `no_room`. `sell(shop, item, n)` pays price times `buyRate` for items the merchant offers or buys, or any priced item when `buysAnything`. - Service `trade.shop`: `shops()`, `open(id)`, `close()`, `current()`, `offers(id)` (with price, stock, affordable, sell price), `priceOf`, `sellPriceOf`, `buy(...)` and `sell(...)` answering `{ ok, reason?, paid? / earned? }`. - Verbs `open_shop { shop }`, `close_shop`, condition `shop_open { shop? }`. Variable `shop.open`. Events `shop.opened`, `shop.closed`, `shop.bought`, `shop.sold`, `shop.refused`. The stock is saved. ## Hooks and modding | Hook | Payload | What a tap may do | | --- | --- | --- | | `money.earn` | `{ amount, reason, balance }` | change `amount`, cancel (nothing is credited) | | `money.spend` | `{ amount, reason, balance }` | change `amount` (before the balance check), cancel (refused like a short balance) | | `shop.open` | `{ shop }` | cancel (a closed shop; `shop.refused` carries the reason) | | `shop.buy` | `{ shop, item, count, price }` | change the total `price` (a discount, a tax), cancel; asked after the stock check and before the balance check | | `shop.sell` | `{ shop, item, count, price }` | change the total `price` the merchant pays, cancel | ```json { "id": "half_rent", "hook": "money.spend", "match": { "reason": "rent" }, "mul": { "amount": 0.5 } } ``` Moddable: every setting above, and the `shops` collection (a registry: `{ "id": "new", "extends": "blacksmith" }` inherits an entry, keys merged, the child wins). Quests and missions pay and check money through the same service (see the Quests guide). ## Engines `economy.money` is the same Rust logic on the web and in the native player, and is delivered to Godot and Unity as files and to Unreal through the shared kit library. `trade.shop` runs on the web and in the native player. The hooks run in those two. ## When it does not do what you expect | Symptom | Cause and fix | | --- | --- | | A purchase is refused | read the reason: `no_room` (inventory full), not enough money, or sold out | | `spend_money` fails at zero | `allowDebt` is `false` | | A reward is not paid | the mission or quest was not completed through the kit, or a `money.earn` tap cancelled it | | Selling pays little | `buyRate` is 0.5 by default; raise it or set a rate on the shop | | The HUD shows the wrong symbol | set `currency` (or seed `economy.currency`) |