> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/data-and-persistence/economy.md).

# Economy

Economy APIs let you create custom currencies (coins, gems, XP, etc) and automatically persist them for each player across sessions.

* Each currency is stored **per-player**
* Balances are **automatically persisted** across sessions
* Balances can be viewed/edited in the creator portal (see [Editing/Viewing Player Data](/data-and-persistence/editing-viewing-player-data.md))

### Economy API reference

```go
Economy :: struct {
    register_currency     :: proc(currency: string, icon: Texture_Asset);
    deposit_currency      :: proc(player: Player, currency: string, amount: s64);
    get_balance           :: proc(player: Player, currency: string) -> s64;
    can_withdraw_currency :: proc(player: Player, currency: string, amount: s64) -> bool;
    withdraw_currency     :: proc(player: Player, currency: string, amount: s64);
    delete_save_data      :: proc(player: Player);
}
```

### Registering a currency (one time)

Before you use a currency name, register it with an icon.

```go
ao_before_scene_load :: proc() {
    // Pick an icon from your /res folder
    coin_icon := get_asset(Texture_Asset, "ui/coin.png");

    Economy.register_currency("Coins", coin_icon);
    Economy.register_currency("XP", coin_icon); // example (use a different icon ideally)
}
```

{% hint style="info" %}
Currency names are just strings. Pick a consistent name and stick to it (for example `"Coins"` vs `"coins"`).
{% endhint %}

### Reading a player's balance

```go
coins := Economy.get_balance(player, "Coins");
```

### Giving currency (rewards)

Use `deposit_currency` whenever a player earns currency.

```go
on_enemy_killed :: proc(player: Player) {
    Economy.deposit_currency(player, "Coins", 10);
    Economy.deposit_currency(player, "XP", 3);
}
```

### Spending currency (shops/upgrades)

Always check `can_withdraw_currency` before withdrawing. Deposit and withdrawal amounts must be non-negative.

```go
UPGRADE_COST :: 50;

try_buy_upgrade :: proc(player: Player) -> bool {
    if !Economy.can_withdraw_currency(player, "Coins", UPGRADE_COST) {
        Notifier.notify(player, "Not enough coins!");
        return false;
    }

    Economy.withdraw_currency(player, "Coins", UPGRADE_COST);

    // Grant the upgrade here...
    Notifier.notify(player, "Upgrade purchased!");
    return true;
}
```

## Built-in shops

A shop contains categories, and each category contains products. Create the shop after registering its currencies. Keep its handles in scene-wide fields or globals because they are valid only for the current scene.

```go
shop: u64;

grant_shop_product :: proc(
    player: Player,
    product: u64,
    userdata: Object
) -> bool {
    if Game_Product.get_id(product) == "health_upgrade" {
        player.max_health += 10;
        return true;
    }

    // Returning false cancels the purchase and does not charge the player.
    return false;
}

ao_before_scene_load :: proc() {
    coin_icon := get_asset(Texture_Asset, "ui/coin.png");
    Economy.register_currency("Coins", coin_icon);

    shop = Economy.create_shop("upgrade_shop");
    upgrades := Shop.add_category(shop, "Upgrades");

    Shop_Category.add_product(
        upgrades,
        "health_upgrade",
        "Health Upgrade",
        "Adds 10 maximum health.",
        "ui/health_upgrade.png",
        "Coins",
        50,
        "",
        Item_Rarity.COMMON.(s64),
        ""
    );

    Shop.set_purchase_handler(shop, null, grant_shop_product);
}
```

The purchase handler must grant the product and return `true`. Returning `false` rejects the purchase.

Draw the shop from player UI code. `Shop.draw` returns `true` while the shop remains open:

```go
Player :: class : Player_Base {
    max_health: int = 100;
    shop_open: bool;

    ao_late_update :: method(dt: float) {
        if !is_local_or_server() return;

        if shop_open {
            shop_open = Shop.draw(shop, UI.get_safe_screen_rect());
        }
    }
}
```

`Shop.set_purchase_modifier` can change a product's price or button per player. `Shop.set_custom_display` can replace product-card drawing. Both callbacks use the same explicit `userdata` pattern as the purchase handler.

### Resetting a player's economy data

If you need to wipe all economy balances for a player (for example, an admin reset button or a game mode reset), you can delete their economy save data:

```go
reset_economy :: proc(player: Player) {
    Economy.delete_save_data(player);
    Notifier.notify(player, "Your economy data was reset.");
}
```

{% hint style="warning" %}
`Economy.delete_save_data` is destructive. Use it sparingly, and consider adding confirmations/admin-only access.
{% endhint %}

### Economy vs Save

* Use **Economy** for "currencies" (coins, gems, XP, tickets) where you want auto-persistence + portal editing.
* Use **Save** for everything else (settings, quest state, unlock lists, complex progress structures). See [Save System](/data-and-persistence/save.md)
