> ## Documentation Index
> Fetch the complete documentation index at: https://docs.humalike.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Acciones definidas por el servidor

> Declara acciones que realiza tu propio script, condicionadas a los hechos que reporta.

El proveedor de acciones declara acciones junto a las observaciones que
[reporta](/es/ai-npc/integrations/world-events#observaciones-del-servidor). El modelo
elige una acción declarada como cualquier acción del catálogo, Humalike acepta la
etiqueta solo una vez que se han reportado las observaciones requeridas, y tu
`RunAction` realiza la acción. Cómo se comportan en juego la condición y el
mostrador se describe en [Sistemas de juego](/es/ai-npc/gameplay#acciones-definidas-por-el-servidor).

```lua theme={null}
exports.humalike:RegisterProvider('actions', {
    name = 'my_actions', apiVersion = 1, priority = 100,
    SupportedActions = {},
    Namespace = 'myserver',
    Observations = {
        item_given = {
            fields = { item = 'string', quantity = 'integer' },
            template = { en = 'the character handed you {quantity} x {item}' },
        },
    },
    Actions = {
        give_map = {
            name = 'Give the treasure map',
            description = 'Hand the player the map to the hidden chest.',
            params = { copies = { type = 'integer', enum = { 1, 2 } } },
            fixed = { item = 'treasure_map' },
            requires = {
                { observation = 'item_given', where = { item = 'amulet' }, consume = true },
            },
            locked_hint = { en = 'Only once the amulet is in your hands.' },
        },
    },
    RunAction = function(action, source, npcCoords, params)
        if action ~= 'give_map' then return false end
        return exports.ox_inventory:AddItem(source, params.item, params.copies or 1)
    end,
})
```

Activa `myserver:give_map` en el panel para los NPC que deban tenerla. Las
claves llevan espacio de nombres en el cable (`myserver:give_map`) y son locales
en `RunAction` (`give_map`). `RunAction` recibe los `params` del modelo
fusionados con `fixed` y `player_id`:

```lua theme={null}
-- Tag: [myserver:give_map copies=2]
-- RunAction receives ('give_map', source, npcCoords, { player_id = 12, copies = 2, item = 'treasure_map' })
```

Las acciones se vuelven a declarar en cada reporte de capacidades. Una acción
modificada o eliminada tiene efecto en el siguiente inicio del recurso.

## Declaración

| Campo         | Descripción                                                                                                                                                                                 |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | Se muestra en el panel. Hasta 80 caracteres.                                                                                                                                                |
| `description` | La línea que el modelo lee bajo sus reglas de ACTIONS. Hasta 400 caracteres, sin corchetes.                                                                                                 |
| `params`      | Hasta 4 valores que rellena el modelo. Consulta [Params](#params).                                                                                                                          |
| `fixed`       | Hasta 16 valores que decide tu script. Nunca se envían a Humalike; se fusionan por debajo de los valores del modelo antes de `RunAction` y siempre prevalecen.                              |
| `requires`    | Hasta 4 condiciones sobre observaciones reportadas, todas las cuales deben cumplirse. Consulta [Condiciones](#condiciones).                                                                 |
| `locked_hint` | Lo que se le dice al NPC, por código de idioma (cualquiera de los que ofrece el panel; se lee el inglés cuando el idioma del NPC no tiene texto), mientras una condición no se cumple.      |
| `limit`       | `{ per_player = 1, every_s = 86400, hint = { en = '...' } }`. Acciones por jugador en cualquier ventana de `every_s` segundos (hasta 7 días). Sin límite salvo que se declare.              |
| `auto`        | `true`: Humalike realiza la acción en la siguiente respuesta del NPC en cuanto se cumplen sus condiciones, escriba o no el modelo la etiqueta. Requiere una condición con `consume = true`. |
| `params_from` | `{ amount = 'item_given.quantity' }`. Valores tomados de los hechos que desbloquean, nunca del modelo. Consulta [Params desde hechos](#params-desde-hechos).                                |
| `uses_stock`  | `{ item = 'map', quantity = 1 }`. La acción toma de las existencias del NPC y se bloquea al llegar a cero. Consulta [Existencias](#existencias).                                            |

### Params

```lua theme={null}
params = {
    copies = { type = 'integer', enum = { 1, 2 }, required = true, description = 'How many copies.' },
}
```

* Los tipos son `string`, `integer` o `boolean`.
* `enum` contiene hasta 16 palabras simples o enteros.
* `required` y `description` son opcionales.
* Humalike añade siempre `player_id`.
* Una etiqueta cuyos valores no encajan con la declaración se descarta antes de
  hablarse o recordarse.

### Condiciones

```lua theme={null}
requires = {
    { observation = 'item_given', where = { item = 'cash', quantity = { gte = 500 } },
      within_s = 600, consume = true },
}
```

| Campo         | Descripción                                                                                        |
| ------------- | -------------------------------------------------------------------------------------------------- |
| `observation` | Una clave de observación declarada, reportada para este NPC y el jugador al que está respondiendo. |
| `where`       | Condiciones sobre campos declarados. Todas deben coincidir.                                        |
| `within_s`    | Antigüedad máxima de los hechos contados, en segundos. De 5 a 3600, por defecto 600.               |
| `consume`     | `true` gasta todos los hechos que la condición contó una vez realizada la acción.                  |

| Forma de `where`             | Coincide con                                       |
| ---------------------------- | -------------------------------------------------- |
| `item = 'amulet'`            | Igualdad escalar. `1` y `1.0` son el mismo número. |
| `quantity = { gte = 500 }`   | Límite inferior en un campo numérico.              |
| `quantity = { lte = 3 }`     | Límite superior. `gte` y `lte` se combinan.        |
| `quantity = { sum_gte = 2 }` | Suma sobre todos los hechos contados.              |

Los hechos gastados desaparecen para todas las acciones y para el mostrador.

### Params desde hechos

```lua theme={null}
refund_deposit = {
    name = 'Give the deposit back',
    description = 'Return the deposit the player put on the counter.',
    requires = { { observation = 'item_given', where = { item = 'cash' }, consume = true } },
    params_from = { amount = 'item_given.quantity' },
},
-- RunAction receives { player_id, amount = <what was actually handed over> }
```

* Los valores provienen de los hechos contados por la **primera** entrada de
  `requires` sobre esa observación.
* Los números se suman sobre esos hechos. Cualquier otra cosa es el valor más
  reciente.

## Existencias

```lua theme={null}
local result = exports.humalike:SetNpcStock(npcId, { map = 50, bread = 'unlimited' })
```

Reemplaza todas las existencias del NPC. Un objeto no listado es uno del que el
NPC no tiene ninguno. El panel muestra las existencias en modo solo lectura en la
página del NPC.

* Hasta 32 objetos por NPC.
* Los nombres de objeto coinciden con `^[a-z][a-z0-9_]*$`, con un máximo de 48 caracteres.
* Las cantidades son números enteros de 0 a 1.000.000, o `'unlimited'`.

Devuelve `{ ok = true, value = { stock } }` o `{ ok = false, error = code }`:

| Error                            | Significado                                                           |
| -------------------------------- | --------------------------------------------------------------------- |
| `invalid_npc`                    | `npcId` no es un string UUID.                                         |
| `npc_not_found`, `npc_not_bound` | Igual que en `ReportObservation`.                                     |
| `invalid_stock`                  | `stock` no es una tabla.                                              |
| `invalid_item:<name>`            | El nombre incumple el patrón o la longitud.                           |
| `invalid_count:<name>`           | La cantidad no es un número entero dentro del rango ni `'unlimited'`. |
| `too_many_items`                 | Más de 32 objetos.                                                    |
| `runtime_not_ready`              | Aún no hay credenciales. No se envió nada.                            |

## Mostrador

Para los NPC que venden, declara un `Catalog` en lugar de una acción por
producto. Humalike toma el pedido, lo tarifica y cobra el pago, y después llama a
`deliver` y `refund` en tu `RunAction`.

```lua theme={null}
Catalog = {
    currency = 'cash',
    payment = 'item_given',
    items = {
        water = { price = 5 },
        bread = { price = 3 },
        pistol = { price = 150, limit = { per_player = 1, every_s = 86400 } },
    },
},
```

| Campo      | Descripción                                                                                                                                                                          |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `currency` | El valor de `item` de la observación de `payment` que cuenta como dinero.                                                                                                            |
| `payment`  | La observación declarada que reporta tu hook de inventario. Su `quantity` debe tener el tipo `integer`.                                                                              |
| `items`    | De 1 a 64 productos, nombrados según la regla de las existencias, cada uno con un `price` entero de 0 a 10.000.000 y un `limit` opcional (misma forma que el `limit` de una acción). |

Un NPC vende los objetos del catálogo que tu script haya [abastecido](#existencias).

### `deliver` y `refund`

Ambas se declaran automáticamente con el `Catalog`. No las listes bajo `Actions`.

| Acción    | `params`                                                                        | Retorno                                                                                                                                           |
| --------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `deliver` | `items` (objeto a cantidad), `total`, `paid`, `change`, `currency`, `player_id` | `true` solo después de que cada línea y el cambio estén en el inventario del jugador. Ante cualquier fallo, deshaz lo añadido y devuelve `false`. |
| `refund`  | `amount`, `currency`, `player_id`                                               | `true` después de que el importe esté de vuelta en el inventario del jugador.                                                                     |

```lua theme={null}
RunAction = function(action, source, npcCoords, params)
    if action == 'deliver' then
        return giveAllOrNothing(source, params.items, params.currency, params.change)
    elseif action == 'refund' then
        return exports.ox_inventory:AddItem(source, params.currency, params.amount) == true
    end
    return false
end
```

### Pedidos desde menú

```lua theme={null}
local result = exports.humalike:PlaceOrder(npcId, playerId, { water = 2 })
```

Envía las líneas elegidas desde tu propio menú de tienda. Hasta 16 líneas, cada
una con una cantidad de 1 a 1.000. Devuelve `{ ok = true, value = { lines } }` o
`{ ok = false, error = code }`:

| Error                                                                      | Significado                                           |
| -------------------------------------------------------------------------- | ----------------------------------------------------- |
| `invalid_player`, `character_not_loaded`, `npc_not_found`, `npc_not_bound` | Igual que en `ReportObservation`.                     |
| `no_catalog`                                                               | El proveedor de acciones no declara ningún `Catalog`. |
| `invalid_lines`                                                            | `lines` no es una tabla no vacía.                     |
| `unknown_item:<name>`                                                      | No está en el catálogo.                               |
| `invalid_quantity:<name>`                                                  | No es un número entero de 1 a 1.000.                  |
| `too_many_lines`                                                           | Más de 16 líneas.                                     |

### Claves reservadas

* `order`, `cancel_order`, `order_placed`, `order_refused`: no se pueden
  declarar como acciones ni como observaciones.
* `deliver`, `refund`: no se pueden declarar como observaciones.
* Ninguna clave puede ser a la vez acción y observación.
* `spent` es un campo de observación reservado.

## Contrato de RunAction

* Devuelve exactamente `true` cuando la acción está hecha. `false` rechaza la
  invocación y no se reintenta.
* Responde en menos de 5 segundos. Una respuesta perdida (timeout, conexión
  caída, HTTP 5xx) se reintenta de inmediato, hasta tres intentos, con el mismo
  id de invocación.
* Conserva los ids que aceptaste y responde a una repetición desde ese registro,
  de modo que una respuesta perdida nunca ejecute una acción dos veces.

## Depuración

* `RegisterProvider` imprime una declaración rechazada con el campo culpable. No
  se registra nada.
* La página del NPC en el panel lista las acciones declaradas con sus
  condiciones. La vista de transcripción muestra observaciones y acciones.
* `humalike_debug 1` registra cada push entrante y su resultado.

## Siguiente

* [Cómo se comportan la condición y el mostrador](/es/ai-npc/gameplay#acciones-definidas-por-el-servidor).
* [Reportar las observaciones de las que dependen estas acciones](/es/ai-npc/integrations/world-events#observaciones-del-servidor).
* [Revisar el contrato de proveedor](/es/ai-npc/integrations/provider-api).
