- Economy System
- Expert guidance for designing balanced game economies with currency, shops, and loot.
- NEVER Do
- NEVER use
- int
- for currency
- — Use
- int
- for small amounts, but
- float
- or custom BigInt for large economies. Integer overflow destroys economies (max 2.1B).
- NEVER forget buy/sell price spread
- — Selling for same price as buying creates infinite money loop. Sell price should be 30-50% of buy price.
- NEVER skip currency sinks
- — Without sinks (repairs, taxes, consumables), economy inflates. Players hoard unlimited wealth.
- NEVER use client-side currency validation
- — Client calculates "I have 1000 gold". Server validates all transactions or exploits occur.
- NEVER hardcode loot drop chances
- — Use Resources or JSON for loot tables. Designers need iteration without code changes.
- Available Scripts
- MANDATORY
- Read the appropriate script before implementing the corresponding pattern. loot_table_weighted.gd Weighted loot table using cumulative probability. Resource-based design allows designer iteration via inspector without code changes. Currency Manager
economy_manager.gd (AutoLoad)
extends Node signal currency_changed ( old_amount : int , new_amount : int ) var gold : int = 0 func add_currency ( amount : int ) -> void : var old := gold gold += amount currency_changed . emit ( old , gold ) func spend_currency ( amount : int ) -> bool : if gold < amount : return false var old := gold gold -= amount currency_changed . emit ( old , gold ) return true func has_currency ( amount : int ) -> bool : return gold
= amount Shop System
shop_item.gd
class_name ShopItem extends Resource @ export var item : Item @ export var buy_price : int @ export var sell_price : int @ export var stock : int = - 1
-1 = infinite
func can_buy ( ) -> bool : return stock != 0
shop.gd
class_name Shop extends Resource @ export var shop_name : String @ export var items : Array [ ShopItem ] = [ ] func buy_item ( shop_item : ShopItem , inventory : Inventory ) -> bool : if not shop_item . can_buy ( ) : return false if not EconomyManager . has_currency ( shop_item . buy_price ) : return false if not EconomyManager . spend_currency ( shop_item . buy_price ) : return false inventory . add_item ( shop_item . item , 1 ) if shop_item . stock
0 : shop_item . stock -= 1 return true func sell_item ( item : Item , inventory : Inventory ) -> bool :
Find matching shop item for sell price
var shop_item := get_shop_item_for ( item ) if not shop_item : return false if not inventory . has_item ( item , 1 ) : return false inventory . remove_item ( item , 1 ) EconomyManager . add_currency ( shop_item . sell_price ) return true func get_shop_item_for ( item : Item ) -> ShopItem : for shop_item in items : if shop_item . item == item : return shop_item return null Pricing Formula func calculate_sell_price ( buy_price : int , markup : float = 0.5 ) -> int :
Sell for 50% of buy price
return int ( buy_price * markup ) func calculate_dynamic_price ( base_price : int , demand : float ) -> int :
Price increases with demand
return int ( base_price * ( 1.0 + demand ) ) Loot Tables
loot_table.gd
class_name LootTable extends Resource @ export var drops : Array [ LootDrop ] = [ ] func roll_loot ( ) -> Array [ Item ] : var items : Array [ Item ] = [ ] for drop in drops : if randf ( ) < drop . chance : items . append ( drop . item ) return items
loot_drop.gd
class_name LootDrop extends Resource @ export var item : Item @ export var chance : float = 0.5 @ export var min_amount : int = 1 @ export var max_amount : int = 1 Best Practices Balance - Test economy carefully Sinks - Provide money sinks (repairs, etc.) Inflation - Control money generation Reference Related: godot-inventory-system , godot-save-load-systems Related Master Skill: godot-master