> 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/all-out-docs/docs-es/conceptos-basicos-del-motor/inventory.md).

# Inventario

### Resumen

All Out proporciona una API de inventario de jugadores que te permite crear objetos, dárselos a los jugadores y permitir que los jugadores los ordenen, usen y suelten en tu juego.

Hay tres conceptos fundamentales:

* **Definiciones de objetos** (`Item_Definition`): qué es un objeto *es* (nombre/icono/apilamiento + tus propios campos)
* **Instancias de objetos** (`Item_Instance`): una pila real en el mundo/dentro de un inventario
* **Inventarios** (`Inventory`): un contenedor de ranuras que contiene instancias de objetos

Cada jugador tiene un inventario integrado en `player.default_inventory`.

### Persistencia

Si quieres que los inventarios de los jugadores se guarden automáticamente entre sesiones de juego, habilita la **Guardado automático del inventario del jugador** casilla en la **Editar → Configuración del juego** sección del editor.

<figure><img src="/files/a0f776ef434b310b053e86ae5ebadf5393a9f489" alt="Player inventory settings in Game Config"><figcaption></figcaption></figure>

{% hint style="info" %}
También puedes configurar estos ajustes programáticamente en CSL. Llama a `Scene.set_auto_save_player_inventory(true)` y `Scene.set_player_inventory_capacity(32)` durante la inicialización de la escena (p. ej., en `ao_before_scene_load`) antes de que se unan los jugadores.
{% endhint %}

### Referencia de la API de inventario

```go
Item_Definition_Desc :: struct {
    id:         string;
    name:       string;
    icon:       Texture_Asset;
    stack_size: s64;    // 1 = no apilable, -1 = infinito
    tier:       Item_Tier;
}

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

    // Definiciones de objetos + instancias
    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);

    // Mover objetos
    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_move_all_items                          :: proc(entries: []Item_Transaction_Entry) -> bool;
    move_all_items                              :: proc(entries: []Item_Transaction_Entry);
    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);

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

    // Interfaz de usuario (opcional)
    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 {
    get_item :: proc(inventory: Inventory, index: s64) -> Item_Instance;
    has_item_id :: proc(inventory: Inventory, item_id: string) -> bool;
    capacity: s64 #read_only;
}

Item_Definition :: class {
    get_icon :: method() -> Texture_Asset;
    id: string #read_only;
    name: string #read_only;
    type: typeid #read_only;
    instance_type: typeid #read_only;
    tier: Item_Tier #read_only;
}

Item_Instance :: class {
    quantity:   s64       #read_only;  // cantidad de la pila
    slot_index: s64       #read_only;  // ranura en el inventario padre
    inventory:  Inventory #read_only;  // inventario padre (nulo si no está en uno)
    get_definition :: method() -> Item_Definition;
}

Item_Transaction_Entry :: struct {
    instance: Item_Instance;
    inventory: Inventory;
}

Inventory_Draw_Options :: struct {
    title: string;
    show_exit_button: bool;
    show_scroll_bar: bool;
    show_background: bool;
    allow_drag_drop: bool;
    drag_drop_color_multiplier: v4;
    hotbar_item_count: s32;
    columns: s32;
    rows: s32;
    force_select_hotbar_index: s32;
    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;
}

Draw_Hotbar_Result :: struct {
    selected_item: Item_Instance;
    selected_item_index: s64;
    dropped_item: Item_Instance;
    entire_rect: Rect;
    inventory_open: bool;
    inventory_open_t: float;
}
```

### Inicio rápido (registrar + dar un objeto)

Registra las definiciones de objetos una sola vez en `ao_before_scene_load`, luego crea instancias y muévelas al inventario de un jugador.

```go
// Opcional: tipos de objetos personalizados
Weapon_Definition :: class : Item_Definition {
    damage: s64;
}

Weapon_Item :: class : Item_Instance {
    durability: s64 @ao_serialize; // @ao_serialize hace que este campo se guarde automáticamente entre sesiones
}

sword_defn: Weapon_Definition;

ao_before_scene_load :: proc() {
    sword_defn = Items.register_item_definition(
        {id="sword", name="Espada de hierro", 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);
        // Si will_destroy es verdadero, el objeto se absorbió en una pila existente
        // y esta referencia al objeto no debe usarse después del movimiento.
    }
    else {
        // Inventario lleno → destruye la instancia que creamos
        Items.destroy_item_instance(item);
    }
}
```

### Mostrar la barra rápida

Para mostrar en pantalla la interfaz estándar de la barra rápida del inventario, llama a `Items.draw_hotbar` dentro del `ao_late_update` método de tu Player. Sin él, el sistema de inventario funciona en segundo plano, ¡pero el jugador no lo verá!

```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 es el objeto actualmente resaltado (o null)
    // result.inventory_open te dice si la interfaz de la mochila completa está visible
}
```

{% hint style="info" %}
`Items.draw_hotbar` gestiona por ti toda la interfaz de la barra rápida + el alternador de la mochila. Devuelve un `Draw_Hotbar_Result` con `selected_item`, `selected_item_index`, `inventory_open`, y `dropped_item` campos.
{% endhint %}

{% hint style="warning" %}
`hide_bag_button = true` impide que el jugador abra la mochila. Si la mochila ya está abierta, se cerrará, así que úsalo solo cuando los jugadores sigan teniendo otra forma de acceder a objetos importantes.
{% endhint %}

### Comprobar si un jugador ya tiene un objeto

Un patrón común es dar un objeto solo si el jugador no tiene ya uno:

Usa el ID registrado de la definición:

```go
give_sword_if_needed :: proc(player: Player) {
    if player.default_inventory.has_item_id("sword") return;
    give_sword(player);
}
```

También puedes comprobar cuánto espacio queda para un tipo de objeto específico antes de crearlo:

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

### Inventarios personalizados

Puedes crear inventarios que no estén vinculados a un jugador, para cosas como:

* Cofres / contenedores de almacenamiento
* Equipos de peces/mobs

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

Para acceder a los objetos:

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

### Movimientos atómicos de objetos

Usa una transacción cuando varios movimientos de objetos deban tener éxito todos o fallar todos:

```go
moves: [..]Item_Transaction_Entry;
moves.append({instance = sword, inventory = player.default_inventory});
moves.append({instance = shield, inventory = player.default_inventory});

if Items.can_move_all_items(moves) {
    Items.move_all_items(moves);
}
```

Cada instancia de objeto solo puede aparecer una vez en la transacción. Llama siempre a `can_move_all_items` primero; `move_all_items` lanza una aserción si la transacción completa ya no cabe. Un movimiento puede fusionar una pila y destruir la instancia movida, así que no sigas usando referencias al objeto después de la transacción.

### Objetos soltados

Si quieres que los jugadores suelten objetos en el mundo (y que otros jugadores puedan recogerlos), usa el sistema de objetos soltados:

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

#### Generar un objeto soltado

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

#### Animación de soltar + ajuste a la navmesh

```go
gameplay_rng: u64;

dropped.do_spawn_animation(ref gameplay_rng, navmesh); // la navmesh es opcional
```

#### Manejar arrastrar y soltar desde la interfaz de la barra rápida

Si usas `Items.draw_hotbar`Dropped\_Item.handle\_dropped\_item `Dropped_Item.handle_dropped_item` lo elimina del inventario y genera una entidad de objeto soltado.

```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 gameplay_rng, navmesh);
}
```

{% hint style="info" %}
Los objetos soltados desaparecen automáticamente con el tiempo (con una barra de advertencia). Mantener pulsado el botón de interactuar sobre un objeto reinicia su temporizador de desaparición.
{% endhint %}

{% hint style="info" %}
Puedes hacer que un objeto soltado sea exclusivo (visible/cogible solo por un jugador) usando `dropped.set_exclusive(player)`.
{% endhint %}
