> For the complete documentation index, see [llms.txt](https://docs.cuxial.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.cuxial.com/scripts/es/nucleo/cuxial-interactions/developers.md).

# Exports y eventos

API pública de Cuxial Interactions: registra NPC, diálogos y tiendas desde tus propios recursos.

Registra tus propios NPC desde cualquier recurso, cuelga un diálogo de un ped que ya creaste, o abre un diálogo o una tienda directamente. Los puntos se registran en el cliente; los precios de una tienda, en el servidor.

## Referencia rápida

| Lado     | Export                             | Devuelve                        |
| -------- | ---------------------------------- | ------------------------------- |
| Cliente  | `Register(id, def)`                | `boolean`                       |
| Cliente  | `Update(id, patch)`                | `boolean`                       |
| Cliente  | `Remove(id)`                       | `boolean`                       |
| Cliente  | `Exists(id)`                       | `boolean`                       |
| Cliente  | `GetEntity(id)`                    | `integer` o `nil`               |
| Cliente  | `Interact(id)`                     | `boolean`                       |
| Cliente  | `ShowDialog(data)`                 | id del botón que cerró, o `nil` |
| Cliente  | `SwitchDialog(dialogId)`           | `boolean`                       |
| Cliente  | `ShowShop(data)`                   | `boolean` o `nil`               |
| Cliente  | `Close()`                          | `boolean`                       |
| Cliente  | `IsOpen()`                         | `boolean`                       |
| Servidor | `RegisterShop(id, items, options)` | `boolean`                       |
| Servidor | `RemoveShop(id)`                   | `boolean`                       |
| Servidor | `GetShop(id)`                      | `table` o `nil`                 |

## Ejemplo completo: un NPC con diálogo y tienda

Un NPC comprador registrado desde un recurso llamado `my_resource`. El cliente registra el punto; el servidor registra qué compra la tienda y a qué precio.

{% stepper %}
{% step %}

### Declara la dependencia

{% code title="my\_resource/fxmanifest.lua" %}

```lua
dependency 'cuxial_interactions'
```

{% endcode %}
{% endstep %}

{% step %}

### Registra la tienda en el servidor

{% code title="my\_resource/server.lua" %}

```lua
local SHOP_ID = 'my_resource:scrap_buyer'

local function registerShop()
    if GetResourceState('cuxial_interactions') ~= 'started' then return end

    exports.cuxial_interactions:RegisterShop(SHOP_ID, {
        { name = 'scrapmetal', price = 12 },
        { name = 'copper', price = 20, label = 'Cable de cobre' },
    }, {
        coords = vec3(25.7, -1347.3, 29.49),
        distance = 6.0,
        account = 'cash',
    })
end

CreateThread(registerShop)

AddEventHandler('onResourceStart', function(resource)
    if resource == 'cuxial_interactions' then registerShop() end
end)

RegisterNetEvent('my_resource:server:AskForWork', function(args, info)
    local src = source
    -- args e info vienen del cliente: comprueba aquí al jugador, la distancia y el estado.
    print(('el jugador %s pidió trabajo en el punto %s'):format(src, info.id))
end)
```

{% endcode %}
{% endstep %}

{% step %}

### Registra el NPC en el cliente

{% code title="my\_resource/client.lua" %}

```lua
local POINT_ID = 'my_resource:scrap_buyer'

local function registerNpc()
    if GetResourceState('cuxial_interactions') ~= 'started' then return end

    exports.cuxial_interactions:Register(POINT_ID, {
        coords = vec4(25.7, -1347.3, 29.49, 271.0),
        model = 's_m_y_dealer_01',
        scenario = 'WORLD_HUMAN_STAND_IMPATIENT',
        behavior = { invincible = true, noTemporaryEvents = true, freeze = true },

        label = 'Hablar con el comprador',
        targetIcon = 'fas fa-comment-dots',
        activeDistance = 2.5,
        blip = { id = 52, scale = 0.6, colour = 5, name = 'Chatarrero' },

        -- shop.id vale por defecto el id del punto, así coincide con RegisterShop.
        shop = { name = 'Sam', job = 'Comprador', icon = 'store' },

        dialog = {
            {
                id = 'inicio',
                name = 'Sam',
                job = 'Comprador',
                icon = 'store',
                text = '¿Traes algo para mí?',
                buttons = {
                    { id = 'vender', label = 'Enseñar lo que llevo', icon = 'hand-coins', shop = true },
                    { id = 'trabajo', label = '¿Tienes trabajo?', icon = 'briefcase', nextDialog = 'trabajo' },
                    { id = 'adios', label = 'Hoy no', icon = 'x', close = true },
                },
            },
            {
                id = 'trabajo',
                name = 'Sam',
                job = 'Comprador',
                text = 'Puede. Tráeme una ganzúa y hablamos.',
                buttons = {
                    {
                        id = 'aceptar',
                        label = 'Tengo una',
                        icon = 'key',
                        item = 'lockpick',
                        close = true,
                        serverEvent = 'my_resource:server:AskForWork',
                        args = { job = 'scrap' },
                    },
                    {
                        id = 'despedirse',
                        label = 'Despedirse',
                        icon = 'hand',
                        close = true,
                        onSelect = function(ctx)
                            PlayAmbientSpeech1(ctx.entity, 'GENERIC_BYE', 'SPEECH_PARAMS_FORCE')
                        end,
                    },
                    { id = 'volver', label = 'Volver', icon = 'undo-2', nextDialog = 'inicio' },
                },
            },
        },
    })
end

CreateThread(registerNpc)

AddEventHandler('onClientResourceStart', function(resource)
    if resource == 'cuxial_interactions' then registerNpc() end
end)
```

{% endcode %}
{% endstep %}
{% endstepper %}

{% hint style="warning" %}
Reiniciar `cuxial_interactions` borra todos los puntos y tiendas registrados por otros recursos. Conserva los manejadores `onClientResourceStart` y `onResourceStart` del ejemplo para que los tuyos se registren de nuevo.
{% endhint %}

## Propiedad y limpieza

* Cada punto y cada tienda pertenece al recurso que lo registró.
* Registrar un id que ya es tuyo lo sustituye. Un id de otro recurso se rechaza con un aviso en consola.
* Solo el dueño puede modificar o quitar un punto o una tienda.
* Al parar un recurso se quitan sus puntos, se cierra su diálogo abierto y se borran sus tiendas.
* Los ids son comunes a todo el servidor. Ponles de prefijo el nombre de tu recurso: `'my_resource:scrap_buyer'`.

## Exports de cliente

### Register

Registra un punto: un NPC, o un lugar sin ped, que abre un diálogo o una tienda.

```lua
exports.cuxial_interactions:Register(id, def)
```

| Parámetro | Tipo   | Descripción                                                           |
| --------- | ------ | --------------------------------------------------------------------- |
| `id`      | string | Id único del punto. No vacío.                                         |
| `def`     | table  | Definición del punto. Consulta [Campos del punto](#campos-del-punto). |

**Devuelve:** `boolean`. `false` cuando `id` o `def` no son válidos, cuando el id es de otro recurso, cuando `def.enabled == false`, o cuando `def` no tiene ninguno de `coords`, `entity` y `netId`.

```lua
-- Cuelga un diálogo de un ped que tu recurso ya creó.
exports.cuxial_interactions:Register('my_resource:doctor', {
    entity = doctorPed,
    label = 'Hablar',
    dialog = {
        {
            id = 'inicio',
            name = 'Dra. Lee',
            text = '¿En qué puedo ayudarte?',
            buttons = { { id = 'adios', label = 'Nada, gracias', close = true } },
        },
    },
})
```

### Update

Mezcla `patch` en la definición de un punto tuyo y lo reconstruye.

```lua
exports.cuxial_interactions:Update(id, patch)
```

| Parámetro | Tipo   | Descripción                                                                                                         |
| --------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `id`      | string | Id del punto.                                                                                                       |
| `patch`   | table  | Campos a sustituir. Solo se mezcla el primer nivel: un `dialog` o un `blip` en el parche sustituye la tabla entera. |

**Devuelve:** `boolean`. `false` cuando el punto no existe o es de otro recurso.

```lua
exports.cuxial_interactions:Update('my_resource:scrap_buyer', { label = 'Cerrado por hoy', dialog = closedDialog })
```

{% hint style="info" %}
`Update` no puede borrar un campo, porque un valor `nil` no viaja en el parche. Para eso llama otra vez a `Register` con la definición completa.
{% endhint %}

### Remove

Quita un punto tuyo: su ped, su blip y su opción de target. Si su diálogo está abierto, se cierra.

```lua
exports.cuxial_interactions:Remove(id)
```

**Devuelve:** `boolean`. `false` cuando el punto no existe o es de otro recurso.

```lua
exports.cuxial_interactions:Remove('my_resource:scrap_buyer')
```

### Exists

Indica si un punto está registrado, sea de quien sea.

```lua
local registered = exports.cuxial_interactions:Exists(id)
```

**Devuelve:** `boolean`.

### GetEntity

Devuelve el ped de un punto.

```lua
local ped = exports.cuxial_interactions:GetEntity(id)
```

**Devuelve:** `integer` o `nil`. `nil` cuando el punto no existe, no tiene ped, o el jugador está lejos y el ped no está creado.

```lua
local ped = exports.cuxial_interactions:GetEntity('my_resource:scrap_buyer')
if ped then PlayAmbientSpeech1(ped, 'GENERIC_HI', 'SPEECH_PARAMS_FORCE') end
```

### Interact

Abre un punto como si el jugador lo hubiera seleccionado. Aplica las mismas condiciones (`job`, `item`, `canInteract`). Úsalo en los puntos con `interaction = 'none'`, que abres desde tu propio marcador, tecla o menú.

```lua
exports.cuxial_interactions:Interact(id)
```

**Devuelve:** `boolean`. `false` cuando el punto no existe, ya hay un diálogo o una tienda abiertos, no se cumplen las condiciones, o el punto no tiene `dialog`, `shop` ni `onInteract`.

```lua
if not exports.cuxial_interactions:Interact('my_resource:scrap_buyer') then
    lib.notify({ description = 'No quiere hablar contigo.', type = 'error' })
end
```

### ShowDialog

Abre un diálogo que no está ligado a un punto registrado. La llamada espera hasta que el diálogo se cierra.

```lua
local result = exports.cuxial_interactions:ShowDialog(data)
```

| Parámetro        | Tipo    | Descripción                                                                 |
| ---------------- | ------- | --------------------------------------------------------------------------- |
| `data.dialog`    | table   | Lista de páginas. Obligatorio. Consulta «Página de diálogo» más abajo.      |
| `data.entity`    | integer | Ped al que enfoca la cámara. Opcional.                                      |
| `data.shop`      | table   | Tienda que abren los botones con `shop = true`. Consulta [Tienda](#tienda). |
| `data.extraData` | table   | Tus propios datos, que llegan en `ctx.extraData`.                           |
| `data.camera`    | boolean | `false` abre sin mover la cámara.                                           |
| `data.id`        | string  | Id que se informa en `ctx.id` y en los eventos. Opcional.                   |

**Devuelve:** el `id` del botón con `close = true` que cerró el diálogo, o su posición si no tiene `id`. `nil` cuando se cerró con <kbd>Esc</kbd> o con `Close()`, cuando los datos no son válidos, o al momento si ya hay algo abierto.

```lua
local answer = exports.cuxial_interactions:ShowDialog({
    entity = guardPed,
    dialog = {
        {
            id = 'pregunta',
            name = 'Guardia',
            text = 'Esta zona está cerrada. ¿Quieres pagar la tasa?',
            buttons = {
                { id = 'si', label = 'Pagar', icon = 'dollar-sign', close = true },
                { id = 'no', label = 'Irme', icon = 'x', close = true },
            },
        },
    },
})

if answer == 'si' then
    TriggerServerEvent('my_resource:server:PayFee')
end
```

{% hint style="info" %}
`ShowDialog` bloquea el hilo que lo llama. Llámalo dentro de `CreateThread` o de un manejador de evento, nunca en un bucle que corre cada frame.
{% endhint %}

### SwitchDialog

Cambia la página del diálogo abierto.

```lua
exports.cuxial_interactions:SwitchDialog(dialogId)
```

| Parámetro  | Tipo             | Descripción                             |
| ---------- | ---------------- | --------------------------------------- |
| `dialogId` | string \| number | `id` de una página del diálogo abierto. |

**Devuelve:** `boolean`. `false` cuando no hay ningún diálogo abierto o la página no existe o está desactivada.

```lua
onSelect = function()
    local hasLicense = lib.callback.await('my_resource:server:HasLicense', false)
    exports.cuxial_interactions:SwitchDialog(hasLicense and 'aprobado' or 'denegado')
end,
```

### ShowShop

Abre una tienda. Con un diálogo abierto, pasa a la tienda y añade un botón para volver. Sin nada abierto, abre la tienda directamente y espera hasta que se cierra.

```lua
exports.cuxial_interactions:ShowShop(data)
```

| Parámetro | Tipo  | Descripción                                                                                                                 |
| --------- | ----- | --------------------------------------------------------------------------------------------------------------------------- |
| `data`    | table | Una tabla de tienda, o `{ shop = tablaDeTienda, entity = ped, camera = boolean, id = string }`. Consulta [Tienda](#tienda). |

**Devuelve:** `boolean` cuando pasó a la tienda desde un diálogo abierto. `nil` cuando la abrió directamente, una vez cerrada.

```lua
exports.cuxial_interactions:ShowShop({
    entity = buyerPed,
    shop = { id = 'my_resource:scrap_buyer', name = 'Sam', job = 'Comprador' },
})
```

{% hint style="warning" %}
Abierta así, la tienda necesita su propio `id`, y ese id debe estar registrado en el servidor con `RegisterShop`. Si no, se muestra como no disponible.
{% endhint %}

### Close

Cierra el diálogo o la tienda que esté abierto.

```lua
exports.cuxial_interactions:Close()
```

**Devuelve:** `boolean`. `false` cuando no había nada abierto.

### IsOpen

Indica si hay un diálogo o una tienda abiertos.

```lua
local open = exports.cuxial_interactions:IsOpen()
```

**Devuelve:** `boolean`.

```lua
if exports.cuxial_interactions:IsOpen() then return end
```

{% hint style="info" %}
El mismo valor está en `LocalPlayer.state.isDialogOpen`. Es local al cliente del jugador y no se envía al servidor.
{% endhint %}

### Alias de compatibilidad

Se conservan nombres antiguos en minúscula para que el código existente siga funcionando. En código nuevo usa los nombres de arriba.

| Alias                           | Equivalente                                                |
| ------------------------------- | ---------------------------------------------------------- |
| `showDialog(data)`              | `ShowDialog(data)`. Acepta además `data.ped` como entidad. |
| `switchDialog(id)`              | `SwitchDialog(id)`                                         |
| `showShop(shop)`                | Pasa a la tienda desde un diálogo abierto.                 |
| `showShopDirect({ ped, shop })` | Abre la tienda directamente.                               |

## Exports de servidor

### RegisterShop

Registra qué compra una tienda y a qué precio. El servidor solo acepta ventas a tiendas registradas, y siempre paga el precio registrado aquí.

```lua
exports.cuxial_interactions:RegisterShop(id, items, options)
```

| Parámetro          | Tipo                        | Descripción                                                                                                                                                                                                                                         |
| ------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`               | string                      | Id de la tienda. El mismo que `shop.id` en el cliente; en un punto registrado con `Register`, el id del punto salvo que pongas otro.                                                                                                                |
| `items`            | table                       | Lista de `{ name, price, label, image }`. `name` es el nombre del objeto y `price` un número de 0 en adelante; los dos son obligatorios. `label` toma por defecto el nombre del inventario. `image` es una URL. Un objeto repetido se toma una vez. |
| `options.coords`   | vector3 \| vector4 \| table | Posición de la tienda, o una lista de posiciones.                                                                                                                                                                                                   |
| `options.netId`    | integer                     | Id de red de un ped en red; la distancia se mide hasta él.                                                                                                                                                                                          |
| `options.anywhere` | boolean                     | `true` acepta ventas desde cualquier distancia.                                                                                                                                                                                                     |
| `options.distance` | number                      | Metros máximos hasta `coords` o hasta el ped. Por defecto, `shop.maxDistance` del config.                                                                                                                                                           |
| `options.account`  | string                      | Cuenta en la que se paga la venta. Por defecto, `shop.account` del config.                                                                                                                                                                          |

Es obligatorio uno de `coords`, `netId` o `anywhere = true`.

**Devuelve:** `boolean`. `false` cuando el id no es válido o es de otro recurso, cuando la lista está vacía o algún objeto no tiene nombre o un precio válido, o cuando no se indica ubicación.

```lua
exports.cuxial_interactions:RegisterShop('my_resource:scrap_buyer', {
    { name = 'scrapmetal', price = 12 },
}, { coords = vec3(25.7, -1347.3, 29.49) })
```

Lo que comprueba el servidor en cada venta, en este orden:

1. La tienda está registrada y compra ese objeto.
2. La cantidad es un número entero entre 1 y `shop.maxQuantity`.
3. El jugador esperó `shop.cooldown` desde su última venta.
4. El jugador está dentro de la distancia de la tienda.
5. El jugador lleva las unidades.

Después quita los objetos y paga `precio × cantidad`, redondeado a un número entero. Si el pago falla, se devuelven los objetos.

{% hint style="info" %}
Con la tienda registrada, el jugador ve la lista y los precios del servidor. Los `items` de la tabla de tienda del cliente solo se muestran cuando la tienda no está registrada, y entonces no se puede vender nada.
{% endhint %}

### RemoveShop

Quita una tienda tuya.

```lua
exports.cuxial_interactions:RemoveShop(id)
```

**Devuelve:** `boolean`. `false` cuando la tienda no existe o es de otro recurso.

### GetShop

Devuelve una tienda registrada.

```lua
local shop = exports.cuxial_interactions:GetShop(id)
```

**Devuelve:** `{ id, account, items }` o `nil`. Cada objeto trae `name`, `label`, `price` e `image`.

```lua
local shop = exports.cuxial_interactions:GetShop('my_resource:scrap_buyer')
if shop then
    for _, item in ipairs(shop.items) do
        print(item.name, item.price)
    end
end
```

## Referencia de datos

### Campos del punto

La tabla que se pasa a `Register`, y el valor de cada NPC en un archivo de `data/`.

**Dónde.** Es obligatorio uno de los tres.

| Campo    | Tipo               | Descripción                                                                                                                                    |
| -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `coords` | vector3 \| vector4 | Posición del punto. Con `model` o `usePlayerSkin` se crea un ped ahí; sin ellos es un lugar sin ped. La `w` de un `vector4` es la orientación. |
| `entity` | integer            | Un ped u objeto que ya existe en este cliente. El punto no lo crea ni lo borra.                                                                |
| `netId`  | integer            | Lo mismo, por id de red. El punto se engancha cuando la entidad entra en rango.                                                                |

**Ped.** Solo en los puntos con `coords` que crean su propio ped.

| Campo           | Tipo             | Descripción                                                                                                                 |
| --------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `model`         | string \| number | Modelo del ped. Si no existe, se usa `npc.fallbackModel`.                                                                   |
| `usePlayerSkin` | boolean          | `true` da al ped el aspecto de un personaje activo al azar. Recurre a `model` cuando no hay ningún aspecto disponible.      |
| `heading`       | number           | Orientación, cuando `coords` es un `vector3`.                                                                               |
| `scenario`      | string           | Escenario que hace el ped, como `'WORLD_HUMAN_CLIPBOARD'`.                                                                  |
| `animation`     | table            | `{ dict, clip, speed, blendOut, duration, flag }`. `dict` y `clip` son obligatorios. Por defecto: `8.0`, `-8.0`, `-1`, `1`. |
| `clothing`      | table            | Lista de `{ componentIndex, variation, texture }`. No se aplica cuando el ped recibió el aspecto de un jugador.             |
| `behavior`      | table            | `{ invincible, noTemporaryEvents, freeze }`, cada uno un boolean.                                                           |
| `spawnDistance` | number           | Metros a los que se crea el ped. Por defecto, `npc.spawnDistance`.                                                          |

**Interacción.**

| Campo            | Tipo    | Descripción                                                                                                                                                                                         |
| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `label`          | string  | Texto de la opción de target. Por defecto, «Hablar».                                                                                                                                                |
| `targetIcon`     | string  | Clase Font Awesome de la opción de target. Por defecto, `target.icon`.                                                                                                                              |
| `activeDistance` | number  | Metros desde los que el jugador puede interactuar. Por defecto, `target.distance`.                                                                                                                  |
| `interaction`    | string  | `'target'`, `'sprite'` o `'none'`. Por defecto, `interaction` del config.                                                                                                                           |
| `camera`         | boolean | `false` abre sin mover la cámara.                                                                                                                                                                   |
| `blip`           | table   | `{ id, scale, colour, name }`. `id` es el sprite del blip. Por defecto: escala `0.6`, color `0`. Necesita `coords`. El blip se crea al registrar el punto, esté a la distancia que esté el jugador. |

**Condiciones.** Deben cumplirse todas para que aparezca la opción.

| Campo         | Tipo            | Descripción                                                                               |
| ------------- | --------------- | ----------------------------------------------------------------------------------------- |
| `job`         | string \| table | `'police'`, `{ 'police', 'ambulance' }` (cualquiera), o `{ police = 2 }` (grado mínimo).  |
| `item`        | string \| table | `'lockpick'`, `{ 'lockpick', 'phone' }` (todos), o `{ lockpick = 3 }` (unidades mínimas). |
| `canInteract` | function        | `function(ctx)`. Debe devolver `true`.                                                    |
| `enabled`     | boolean         | `false` deja el punto sin registrar.                                                      |

**Contenido.**

| Campo        | Tipo     | Descripción                                                                                                             |
| ------------ | -------- | ----------------------------------------------------------------------------------------------------------------------- |
| `dialog`     | table    | Lista de páginas. Se muestra la primera página activa.                                                                  |
| `shop`       | table    | Tienda del punto. Sin `dialog`, el punto abre la tienda directamente; con `dialog`, la abre un botón con `shop = true`. |
| `onInteract` | function | `function(ctx)`. Se ejecuta en lugar de abrir el diálogo o la tienda.                                                   |
| `extraData`  | table    | Tus propios datos, que llegan en `ctx.extraData`.                                                                       |

### Página de diálogo

| Campo       | Tipo             | Descripción                                                        |
| ----------- | ---------------- | ------------------------------------------------------------------ |
| `id`        | string \| number | Único dentro del diálogo. Lo usan `nextDialog` y `SwitchDialog`.   |
| `name`      | string           | Quién habla.                                                       |
| `job`       | string           | Cargo que aparece en la cabecera. Solo texto, no es una condición. |
| `text`      | string           | Lo que dice el NPC.                                                |
| `textSpeed` | number           | Milisegundos por carácter. Por defecto, `dialog.textSpeed`.        |
| `icon`      | string           | Icono de la cabecera. Consulta [Iconos](#iconos).                  |
| `enabled`   | boolean          | `false` omite la página.                                           |
| `buttons`   | table            | Lista de botones.                                                  |

### Botón de diálogo

| Campo                        | Tipo             | Descripción                                                                                               |
| ---------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------- |
| `id`                         | string \| number | Id del botón. Es lo que devuelve `ShowDialog` cuando el botón cierra.                                     |
| `label`                      | string           | Texto del botón.                                                                                          |
| `icon`                       | string           | Icono del botón. Consulta [Iconos](#iconos).                                                              |
| `enabled`                    | boolean          | `false` oculta el botón.                                                                                  |
| `job`, `item`, `canInteract` |                  | Condiciones, como en el punto. Un botón que no las cumple no se envía a la interfaz ni se puede ejecutar. |
| `close`                      | boolean          | Cierra el diálogo.                                                                                        |
| `onSelect`                   | function         | `function(ctx)`.                                                                                          |
| `event`                      | string           | Evento de cliente: `TriggerEvent(event, args, info)`.                                                     |
| `serverEvent`                | string           | Evento de servidor: `TriggerServerEvent(serverEvent, args, info)`.                                        |
| `args`                       | any              | Valor que se envía como primer argumento de `event` y `serverEvent`.                                      |
| `shop`                       | boolean          | Abre la tienda del punto.                                                                                 |
| `nextDialog`                 | string \| number | `id` de la página que se muestra a continuación.                                                          |

Un botón ejecuta sus acciones en este orden: `close`, `onSelect`, `event`, `serverEvent`, y después `shop` o `nextDialog`. Con `close = true` se omite el último paso. `shop` tiene prioridad sobre `nextDialog`.

### Tienda

| Campo   | Tipo   | Descripción                                                                                                                                                                                               |
| ------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`    | string | Id registrado con `RegisterShop`. En un punto vale por defecto el id del punto.                                                                                                                           |
| `name`  | string | Nombre del comprador. Por defecto, «Vendedor».                                                                                                                                                            |
| `job`   | string | Cargo que aparece en la cabecera. Por defecto, «Comerciante».                                                                                                                                             |
| `icon`  | string | Icono de la cabecera. Consulta [Iconos](#iconos).                                                                                                                                                         |
| `items` | table  | Lista de `{ name, label, price, image }`. Obligatoria en los archivos de `data/`, donde es también la lista que se registra en el servidor. Desde otro recurso es opcional: se usa la lista del servidor. |

### La tabla `ctx`

La reciben `onSelect`, `canInteract` y `onInteract`.

| Campo       | Tipo             | Descripción                                                               |
| ----------- | ---------------- | ------------------------------------------------------------------------- |
| `id`        | string           | Id del punto. `nil` en un diálogo abierto con `ShowDialog` sin `data.id`. |
| `entity`    | integer          | Ped del punto, si lo hay. `ctx.ped` contiene el mismo valor.              |
| `dialogId`  | string \| number | `id` de la página actual. Solo en botones.                                |
| `buttonId`  | string \| number | `id` del botón. Solo en botones.                                          |
| `index`     | integer          | Posición del botón en la página. Solo en botones.                         |
| `extraData` | table            | El `extraData` del punto o de `ShowDialog`.                               |

### La tabla `info`

Segundo argumento de `event` y `serverEvent`.

| Campo      | Tipo             | Descripción                                                                                               |
| ---------- | ---------------- | --------------------------------------------------------------------------------------------------------- |
| `id`       | string           | Id del punto.                                                                                             |
| `dialogId` | string \| number | `id` de la página.                                                                                        |
| `buttonId` | string \| number | `id` del botón.                                                                                           |
| `entity`   | integer          | Handle del ped en el cliente que lo envió.                                                                |
| `netId`    | integer          | Id de red del ped. Solo cuando el ped está en red; los peds que crea un punto son locales y no lo tienen. |

{% hint style="danger" %}
Cualquier cliente puede lanzar un evento de servidor en cualquier momento. En su manejador, comprueba al jugador (`source`), la distancia al NPC y lo que la acción requiera. No te fíes nunca de `args` ni de `info`.
{% endhint %}

### Funciones enviadas a través de exports

`onSelect`, `canInteract` y `onInteract` son funciones de tu recurso a las que llama Cuxial Interactions. Ten en cuenta:

* El `canInteract` de un punto se evalúa mientras el jugador le apunta, reutilizando el resultado durante `dialog.conditionInterval` milisegundos, y una vez más al seleccionar. Que sea ligero.
* Un error dentro de cualquiera de ellas no rompe el diálogo. Deja un aviso en la consola del cliente.
* No guardes `ctx` después de que la llamada termine.
* Cuando baste con un evento, usa `event` o `serverEvent`: no dependen de referencias a funciones.

### Iconos

`targetIcon` recibe una clase Font Awesome, porque lo dibuja tu recurso de target.

El `icon` de páginas, botones y tiendas recibe uno de estos nombres:

`arrow-left`, `arrow-right`, `bed`, `book`, `book-open`, `briefcase`, `car`, `check`, `circle-alert`, `circle-check`, `circle-play`, `circle-question-mark`, `circle-x`, `clipboard`, `clipboard-plus`, `dollar-sign`, `door-open`, `fish`, `folder-open`, `gamepad-2`, `hand`, `hand-coins`, `hand-heart`, `heart`, `hospital`, `house`, `id-card`, `info`, `key`, `log-out`, `message-circle`, `message-circle-more`, `messages-square`, `package`, `phone`, `pill`, `shield`, `shopping-cart`, `skull`, `stethoscope`, `store`, `thumbs-up`, `undo-2`, `user`, `user-pen`, `venetian-mask`, `wrench`, `x`.

Cualquier otro nombre muestra un bocadillo de diálogo. En la cabecera de una tienda muestra una tienda.

## Eventos

Cuxial Interactions no tiene eventos propios para otros recursos. Usa los campos `event` y `serverEvent` de un botón para recibir los tuyos.


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.cuxial.com/scripts/es/nucleo/cuxial-interactions/developers.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
