> 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/core-engine-concepts/inventory.md).

# Inventory

### Overview

All Out provides a player inventory API that allows you to create items, give them to players, and for players to arrange, use, and drop items in your game.

There are three core concepts:

* **Item definitions** (`Item_Definition`): what an item *is* (name/icon/stacking + your own fields)
* **Item instances** (`Item_Instance`): an actual stack in the world/in an inventory
* **Inventories** (`Inventory`): a container of slots that holds item instances

Every player has a built-in inventory at `player.default_inventory`.

### Persistence

If you'd like player inventories to be automatically saved across game sessions, enable the "Auto Save Player Inventory" box in the Edit -> Game Settings section of the editor

<figure><img src="/files/CW4OoQfwg0HZx7BCJrbc" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
You can also configure these settings programmatically in CSL. Call `Scene.set_auto_save_player_inventory(true)` and `Scene.set_player_inventory_capacity(32)` during scene initialisation (e.g. in `ao_before_scene_load`) before any players join.
{% endhint %}

### Inventory API reference

```go
Item_Definition_Desc :: struct {
    id:         string;
    name:       string;
    icon:       Texture_Asset;
    stack_size: s64;    // 1 = not stackable, -1 = infinite
    tier:       Item_Tier;
}

Items :: struct {
    // Inventories
    create_inventory  :: proc(unique_id: string, capacity: s64) -> Inventory;
    destroy_inventory :: proc(inventory: Inventory) -> bool;
    set_capacity      :: proc(inventory: Inventory, capacity: s64);

    // Item definitions + instances
    register_item_definition :: proc(desc: Item_Definition_Desc, $Definition_Type: typeid = Item_Definition, instance_type: typeid = Item_Instance) -> Definition_Type;
    create_item_instance     :: proc(definition: Item_Definition, count: s64 = 1) -> Item_Instance;
    create_item_instance     :: proc(definition: Item_Definition, $T: typeid, count: s64 = 1) -> T;
    destroy_item_instance    :: proc(instance: Item_Instance, count: s64 = -1);

    // Moving items around
    can_move_item_to_inventory                  :: proc(instance: Item_Instance, inventory: Inventory, will_destroy_item: ref bool) -> bool;
    move_item_to_inventory                      :: proc(instance: Item_Instance, inventory: Inventory);
    move_as_many_items_as_possible_to_inventory :: proc(instance: Item_Instance, inventory: Inventory, destroyed_item: ref bool) -> s64;
    remove_item_from_inventory                  :: proc(instance: Item_Instance, inventory: Inventory);
    can_swap_items                              :: proc(inventory_a: Inventory, inventory_b: Inventory, slot_a: s64, slot_b: s64) -> bool;
    swap_items                                  :: proc(inventory_a: Inventory, inventory_b: Inventory, slot_a: s64, slot_b: s64);

    // Queries
    calculate_room_in_inventory_for_item :: proc(definition: Item_Definition, inventory: Inventory) -> s64;
    destroy_all_items :: proc(inventory: Inventory);

    // UI (optional)
    draw_inventory :: proc(rect: Rect, inventory: Inventory, options: Inventory_Draw_Options) -> bool;
    draw_hotbar    :: proc(player: Player, inventory: Inventory, options: Inventory_Draw_Options) -> Draw_Hotbar_Result;
}

Inventory :: class : Inventory_Base {
    get_item :: proc(inventory: Inventory, index: s64) -> Item_Instance;
}

Item_Definition :: class : Item_Definition_Base {
    get_name :: method() -> string;
    get_id   :: method() -> string;
    get_icon :: method() -> Texture_Asset;
}

Item_Instance :: class : Item_Instance_Base {
    quantity:   s64       #read_only;  // stack count
    slot_index: s64       #read_only;  // slot in the parent inventory
    inventory:  Inventory #read_only;  // parent inventory (null if not in one)
    get_definition :: method() -> Item_Definition;
}

Inventory_Draw_Options :: struct {
    title: string;
    show_exit_button: bool;
    show_scroll_bar: bool;
    show_background: bool;
    allow_drag_drop: bool;
    hide_bag_button: bool;
    enable_selection: bool;
    scroll_item_selection: bool;
    keyboard_item_selection: bool;
    enable_use_from_hotbar: bool;
    on_before_draw: (proc(item: Item_Instance, rect: Rect));
    on_after_draw: (proc(item: Item_Instance, rect: Rect));

    default :: proc() -> Inventory_Draw_Options;
}
```

### Quick start (register + give an item)

Register item definitions once in `ao_before_scene_load`, then create instances and move them into a player’s inventory.

```go
// Optional: custom item types
Weapon_Definition :: class : Item_Definition {
    damage: s64;
}

Weapon_Item :: class : Item_Instance {
    durability: s64 @ao_serialize; // @ao_serialize makes it automatically save this field across sessions
}

sword_defn: Weapon_Definition;

ao_before_scene_load :: proc() {
    sword_defn = Items.register_item_definition(
        {id="sword", name="Iron Sword", icon=get_asset(Texture_Asset, "icons/sword.png"), stack_size=1, tier=.COMMON},
        Weapon_Definition,
        Weapon_Item
    );
    sword_defn.damage = 10;
}

give_sword :: proc(player: Player) {
    item := Items.create_item_instance(sword_defn, Weapon_Item);

    will_destroy: bool;
    if Items.can_move_item_to_inventory(item, player.default_inventory, ref will_destroy) {
        item.durability = 100;
        Items.move_item_to_inventory(item, player.default_inventory);
        // If will_destroy is true, the item was absorbed into an existing stack
        // and this item reference should not be used after the move.
    }
    else {
        // Inventory full → destroy the instance we created
        Items.destroy_item_instance(item);
    }
}
```

### Displaying the hotbar

To show the standard inventory hotbar UI on screen, call `Items.draw_hotbar` inside your Player's `ao_late_update` method. Without it, the inventory system works behind the scenes but the player won't see it!

```go
Player :: class : Player_Base {
    ao_late_update :: method(dt: float) {
        if this.is_local_or_server() {
            draw_player_hotbar(this);
        }
    }
}

draw_player_hotbar :: proc(player: Player) {
    options := Inventory_Draw_Options.default();
    options.hide_bag_button = false;

    result := Items.draw_hotbar(player, player.default_inventory, options);

    // result.selected_item is the currently highlighted item (or null)
    // result.inventory_open tells you if the full bag UI is showing
}
```

{% hint style="info" %}
`Items.draw_hotbar` handles the entire hotbar + bag toggle UI for you. It returns a `Draw_Hotbar_Result` with `selected_item`, `selected_item_index`, `inventory_open`, and `dropped_item` fields.
{% endhint %}

{% hint style="warning" %}
`hide_bag_button = true` prevents the player from opening the backpack. If the backpack is already open, it will close, so only use it when players still have another way to access important items.
{% endhint %}

### Checking if a player already has an item

A common pattern is to give an item only if the player doesn't already have one:

```go
player_has_item :: proc(player: Player, definition: Item_Definition) -> bool {
    inv := player.default_inventory;
    for i: 0..<inv.capacity {
        item := inv.get_item(i);
        if item != null && item.get_definition() == definition {
            return true;
        }
    }
    return false;
}

give_sword_if_needed :: proc(player: Player) {
    if player_has_item(player, sword_defn) return;
    give_sword(player);
}
```

You can also check how much room is left for a specific item type before creating it:

```go
room := Items.calculate_room_in_inventory_for_item(sword_defn, player.default_inventory);
if room > 0 {
    give_sword(player);
}
```

### Custom Inventories

You can create inventories that aren’t tied to a player, for things like:

* Chests / storage containers
* Fish/mob teams

```go
chest_inventory := Items.create_inventory("chest_01", 12);
```

To access items:

```go
for i: 0..<chest_inventory.capacity {
    item := chest_inventory.get_item(i);
    if item == null continue;
    defn := item.get_definition();
    log_info("Slot %: %", {i, defn.get_name()});
}
```

### Dropped Items

If you want players to drop items into the world (and let other players pick them up), use the dropped items system:

```go
import "core:dropped_items"
```

#### Spawning a dropped item

```go
item := Items.create_item_instance(sword_defn, Weapon_Item);
dropped := Dropped_Item.spawn(player.entity.world_position, item);
```

#### Drop animation + snapping to navmesh

```go
server_rng: u64;

dropped.do_spawn_animation(ref server_rng, navmesh); // navmesh is optional
```

#### Handling drag-drop from the hotbar UI

If you use `Items.draw_hotbar`, players can drag an item out of the UI to drop it. `Dropped_Item.handle_dropped_item` removes it from the inventory and spawns a dropped item entity.

```go
result := Items.draw_hotbar(player, player.default_inventory, Inventory_Draw_Options.default());

dropped: Dropped_Item;
if Dropped_Item.handle_dropped_item(result, player.entity.world_position, ref dropped) {
    dropped.do_spawn_animation(ref server_rng, navmesh);
}
```

{% hint style="info" %}
Dropped items automatically despawn over time (with a warning bar). Holding the interact button on an item resets its despawn timer.
{% endhint %}

{% hint style="info" %}
You can make a dropped item exclusive (only visible/pickable by one player) using `dropped.set_exclusive(player)`.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.allout.game/core-engine-concepts/inventory.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
