> 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/ocio/cuxial-gym/developers.md).

# Exports y eventos

API pública de Cuxial Gym: exports, eventos de servidor, hooks y state bags para otros recursos.

Lee las estadísticas de un jugador, concede membresías, mueve el dinero de un negocio o reacciona a un entrenamiento desde tus propios recursos.

En todos los exports, `cid` es el identificador de personaje que usa tu framework, y los ids de negocio y de zona son los números que muestran los editores de staff.

## Exports de servidor · jugadores

### GetStats

Devuelve los niveles y la XP de un jugador conectado.

```lua
local stats = exports.cuxial_gym:GetStats(source)
```

**Devuelve:** `{ strength = { level, xp }, agility = { level, xp } }`, o `nil` si el jugador no está cargado.

```lua
local stats = exports.cuxial_gym:GetStats(source)
if stats and stats.strength.level >= 25 then
    -- tiene fuerza para cargar la caja
end
```

### AddStatXp

Suma XP a una estadística de un jugador conectado.

```lua
local levels = exports.cuxial_gym:AddStatXp(source, stat, xp)
```

| Parámetro | Tipo   | Descripción                                |
| --------- | ------ | ------------------------------------------ |
| `source`  | number | Id de servidor del jugador.                |
| `stat`    | string | `'strength'` o `'agility'`.                |
| `xp`      | number | XP que se suma. Tiene que ser mayor que 0. |

**Devuelve:** `number`. Niveles ganados; `0` si no se sumó nada.

```lua
exports.cuxial_gym:AddStatXp(source, 'agility', 150)
```

### GetFatigue

Devuelve la fatiga actual de cada parte del cuerpo de un jugador conectado.

```lua
local fatigue = exports.cuxial_gym:GetFatigue(source)
```

**Devuelve:** `table<string, number>` por parte del cuerpo (`chest`, `shoulders`, `back`, `arms`, `abs`, `thighs`, `calves`), o `nil`.

### GrantMembership

Da a un jugador un pase para una zona. Funciona con personajes desconectados.

```lua
local ok, reason = exports.cuxial_gym:GrantMembership(target, zoneId, days, packageId)
```

| Parámetro   | Tipo             | Descripción                                            |
| ----------- | ---------------- | ------------------------------------------------------ |
| `target`    | number \| string | Id de servidor, o `cid` del personaje.                 |
| `zoneId`    | number           | Zona del pase.                                         |
| `days`      | number           | Duración, de 1 a 3650.                                 |
| `packageId` | number           | Opcional. Paquete con el que queda registrado el pase. |

**Devuelve:** `boolean` y, si falla, un motivo: `'not_ready'`, `'unknown_zone'`, `'invalid_days'` o `'player_not_found'`.

```lua
exports.cuxial_gym:GrantMembership(source, 1, 30)
```

### RevokeMembership

Quita a un jugador el pase de una zona.

```lua
local ok, reason = exports.cuxial_gym:RevokeMembership(target, zoneId)
```

**Devuelve:** `boolean` y, si falla, `'not_ready'`, `'unknown_zone'`, `'player_not_found'` o `'no_membership'`.

### GetMembership

Devuelve el pase activo de un jugador en una zona.

```lua
local pass = exports.cuxial_gym:GetMembership(target, zoneId)
```

**Devuelve:** `{ zoneId, businessId, packageId, expiresAt, purchasedAt, secondsLeft }`, o `nil` si no hay pase activo. Las fechas son marcas Unix en segundos.

```lua
local pass = exports.cuxial_gym:GetMembership(source, 1)
if pass then print(('quedan %d horas'):format(pass.secondsLeft // 3600)) end
```

## Exports de servidor · negocios

### Lectura

| Export                             | Devuelve                                                                                                   |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `GetBusinessInfo(id)`              | `{ id, name, type, logo, color, funds, level, xp, maxXp, ownerName }`, o `nil`. `type` es siempre `'gym'`. |
| `GetBusiness(id)`                  | Copia completa del negocio, tal como la muestra el panel del negocio, o `nil`.                             |
| `GetAllBusinesses()`               | Tabla de copias por id de negocio.                                                                         |
| `GetBusinessesByType(kind)`        | La misma tabla cuando `kind` es `'gym'`; vacía en otro caso.                                               |
| `GetBusinessFunds(id)`             | `number`. `0` si el negocio no existe.                                                                     |
| `GetBusinessLevel(id)`             | `{ level, xp, maxXp }`, o `nil`.                                                                           |
| `GetEmployees(id)`                 | Lista de `{ identifier, name, avatar, grade, minutesWorked, repsHelped, pendingBonus, totalBonus }`.       |
| `GetEmployeeCount(id)`             | `number`.                                                                                                  |
| `GetWarehouse(id)`                 | Lista de `{ name, stock, enabled }`.                                                                       |
| `GetWarehouseStock(id, item)`      | `number`.                                                                                                  |
| `GetBusinessInteractionPoints(id)` | Lista de `{ id, scope, ownerId, mode, model, coords, rotation, radius, actions }`.                         |
| `GetBusinessMetadata(id)`          | Copia de los metadatos del negocio, o `nil`.                                                               |

```lua
local info = exports.cuxial_gym:GetBusinessInfo(1)
if info then print(info.name, info.funds, info.level) end
```

### Miembros

| Export                               | Devuelve                                                          |
| ------------------------------------ | ----------------------------------------------------------------- |
| `GetPlayerBusinesses(cid)`           | `number[]`. Ids de los negocios a los que pertenece el personaje. |
| `IsPlayerInBusiness(cid, id)`        | `boolean`.                                                        |
| `IsPlayerOwner(cid, id)`             | `boolean`.                                                        |
| `GetPlayerRole(cid, id)`             | `string`. Identificador del rango, o `nil`.                       |
| `HasPermission(cid, id, permission)` | `boolean`.                                                        |

`permission` es uno de `'manage_company'`, `'manage_employees'`, `'manage_permissions'`, `'manage_warehouse'`, `'manage_finances'`, `'manage_sellers'`, `'pay_bonuses'`, `'transfer_ownership'`, o su posición en esa lista (de 1 a 8).

```lua
if exports.cuxial_gym:HasPermission(cid, 1, 'manage_finances') then
    -- puede tocar el dinero
end
```

### HirePlayer

Añade un personaje a un negocio sin oferta previa.

```lua
local ok = exports.cuxial_gym:HirePlayer(id, cid, name, grade)
```

| Parámetro | Tipo   | Descripción                                                  |
| --------- | ------ | ------------------------------------------------------------ |
| `id`      | number | Id del negocio.                                              |
| `cid`     | string | Identificador del personaje.                                 |
| `name`    | string | Nombre que aparece en la lista de empleados.                 |
| `grade`   | string | Opcional. Identificador del rango; `'employee'` por defecto. |

**Devuelve:** `boolean`. `false` si el personaje ya trabaja ahí, el rango no existe o el negocio no tiene plazas de empleado libres.

### FirePlayer

```lua
local ok = exports.cuxial_gym:FirePlayer(id, cid)
```

**Devuelve:** `boolean`. `false` si el personaje no está en el negocio.

### Dinero

| Export                                          | Qué hace                                                                                                   |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `AddBusinessIncome(id, amount, reason, source)` | Registra un ingreso. Cuenta para impuestos, XP del negocio y misiones. `reason` y `source` son opcionales. |
| `DepositToBusiness(id, amount, source)`         | Añade fondos como depósito. `source` es opcional.                                                          |
| `WithdrawFromBusiness(id, amount, source)`      | Saca fondos. Falla si no hay suficiente.                                                                   |

Los tres devuelven `boolean`. `amount` es un número entero mayor que 0. `source`, si se indica, es el jugador que queda como autor de la transacción.

{% hint style="warning" %}
Estos exports solo cambian los fondos del negocio. No dan ni quitan dinero a ningún jugador: eso hazlo en tu propio código.
{% endhint %}

```lua
if exports.cuxial_gym:AddBusinessIncome(1, 2500, 'Clase privada', source) then
    -- cobra al jugador aquí
end
```

### Stock, XP y metadatos

| Export                                   | Qué hace                                           |
| ---------------------------------------- | -------------------------------------------------- |
| `AddWarehouseStock(id, item, amount)`    | Añade unidades de un item al almacén.              |
| `RemoveWarehouseStock(id, item, amount)` | Quita unidades. Falla si no hay suficientes.       |
| `AddBusinessXP(id, amount)`              | Suma XP al negocio.                                |
| `SetBusinessMetadata(id, key, value)`    | Define una clave de los metadatos. `nil` la borra. |

Los cuatro devuelven `boolean`.

`SetBusinessMetadata` acepta solo estas claves, con estos tipos: `workArea` (table), `workerLevels` (table), `membership` (table), `incomeTax` (number), `xpRate` (number), `helperRepsPerMinigame` (number) y `callCooldownUntil` (number).

## Exports de cliente

| Export                   | Devuelve      | Qué hace                                                                                                                |
| ------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `IsExercising()`         | boolean       | Si el jugador local está en una serie.                                                                                  |
| `IsHelping()`            | boolean       | Si el jugador local está ayudando a alguien.                                                                            |
| `OpenGymPanel(zoneId)`   | boolean       | Abre el panel del gimnasio de una zona. El jugador tiene que estar cerca de un punto del menú del gimnasio de esa zona. |
| `OpenBusinessPanel(id)`  | boolean       | Abre el panel del negocio. Sin `id`, abre el negocio del jugador o una lista para elegir.                               |
| `CloseBusinessPanel()`   | boolean       | Lo cierra.                                                                                                              |
| `IsBusinessPanelOpen()`  | boolean       | Si el panel del negocio está abierto.                                                                                   |
| `GetCurrentBusinessId()` | number \| nil | Id del negocio que muestra el panel abierto.                                                                            |
| `IsInBusiness(id)`       | boolean       | Si el jugador local es personal de ese negocio.                                                                         |

```lua
if exports.cuxial_gym:IsExercising() then return end
```

### UseGizmo

Deja al jugador mover y rotar una entidad con el gizmo de colocación, y espera a que confirme o cancele.

```lua
local result = exports.cuxial_gym:UseGizmo(entity, { camera = true })
```

| Parámetro     | Tipo    | Descripción                                              |
| ------------- | ------- | -------------------------------------------------------- |
| `entity`      | number  | Entidad que se coloca.                                   |
| `opts.camera` | boolean | Opcional. `true` usa la cámara de colocación de `gizmo`. |

**Devuelve:** `{ position = vector3, rotation = vector3, cancelled = boolean }`, o `nil` si la entidad no existe.

```lua
local result = exports.cuxial_gym:UseGizmo(prop)
if result and not result.cancelled then
    print(result.position, result.rotation)
end
```

## Eventos de servidor

Se lanzan solo en el servidor. Escúchalos con `AddEventHandler`.

| Evento                        | Parámetros                | Cuándo                                                                                       |
| ----------------------------- | ------------------------- | -------------------------------------------------------------------------------------------- |
| `cuxial_gym:exerciseStarted`  | `source, ctx`             | Un jugador empieza una serie.                                                                |
| `cuxial_gym:exerciseFinished` | `source, ctx`             | Una serie termina, por el motivo que sea.                                                    |
| `cuxial_gym:statLevelUp`      | `source, data`            | Un jugador sube de nivel. `data` es `{ strength, agility, gained = { strength, agility } }`. |
| `cuxial_gym:stockChanged`     | `businessId, item, stock` | Cambia el stock de un item en un almacén.                                                    |

Campos de `ctx`:

| Campo        | Tipo            | Descripción                                                                                                                                                                  |
| ------------ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `exerciseId` | string          | Ejercicio, como en `data/exercises.lua`.                                                                                                                                     |
| `eqId`       | string          | Equipo.                                                                                                                                                                      |
| `zoneId`     | number \| false | Zona; `false` en un modelo global.                                                                                                                                           |
| `zoneName`   | string \| false | Nombre de la zona.                                                                                                                                                           |
| `businessId` | number \| false | Negocio dueño de la zona.                                                                                                                                                    |
| `bodyPart`   | string          | Parte del cuerpo principal que se entrena.                                                                                                                                   |
| `maxReps`    | number          | Límite de repeticiones del ejercicio; `0` es sin límite.                                                                                                                     |
| `global`     | boolean         | `true` en un prop del mapa fuera de cualquier zona.                                                                                                                          |
| `reps`       | number          | Repeticiones hechas. Solo al terminar.                                                                                                                                       |
| `freeReps`   | number          | Repeticiones extra conseguidas. Solo al terminar.                                                                                                                            |
| `levels`     | table           | Niveles ganados: `{ strength, agility }`. Solo al terminar.                                                                                                                  |
| `durationMs` | number          | Duración de la serie. Solo al terminar.                                                                                                                                      |
| `reason`     | string          | Por qué terminó: `esc_cancel`, `minigame_fail`, `max_reps`, `fatigue_overload`, `too_far`, `config_stop`, `anim_failed`, `server_ended` o `resource_stop`. Solo al terminar. |

```lua
AddEventHandler('cuxial_gym:exerciseFinished', function(source, ctx)
    if ctx.reps >= 20 then
        -- premia una serie larga
    end
end)
```

## Hooks

Dos archivos quedan abiertos para tu código: `server/hooks.lua` y `client/hooks.lua`. Rellena el cuerpo de cada función; un error dentro de un hook se captura y se muestra en la consola.

### Servidor

| Hook                                    | Se llama                                         | Retorno                                                                                    |
| --------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `hooks.onPlayerLoaded(src, cid, stats)` | Se cargan los datos de gimnasio de un personaje. | Ninguno                                                                                    |
| `hooks.onStatLevelUp(src, cid, stats)`  | El jugador sube de nivel.                        | Ninguno                                                                                    |
| `hooks.canStartExercise(src, ctx)`      | Antes de empezar una serie.                      | `false` la bloquea. Un segundo valor indica la clave de idioma del mensaje que se muestra. |
| `hooks.onExerciseStarted(src, ctx)`     | Empieza una serie.                               | Ninguno                                                                                    |
| `hooks.onExerciseFinished(src, ctx)`    | Termina una serie.                               | Ninguno                                                                                    |

`stats` es `{ strength = { level, xp }, agility = { level, xp } }`. `ctx` es la misma tabla que en los eventos.

{% code title="server/hooks.lua" %}

```lua
function hooks.canStartExercise(src, ctx)
    -- no se entrena fuera del mundo principal
    if GetPlayerRoutingBucket(src) ~= 0 then
        return false
    end
    return true
end
```

{% endcode %}

### Cliente

| Hook                        | Se llama                                      | Retorno                   |
| --------------------------- | --------------------------------------------- | ------------------------- |
| `hooks.onExerciseRep(data)` | Después de cada repetición del jugador local. | `false` termina la serie. |

`data` es `{ exerciseId, eqId, zoneId, reps, freeReps, maxReps, bodyPart }`.

## State bags

Los define el servidor en cada jugador y se replican a los clientes. Son de solo lectura: un cambio hecho desde un cliente se revierte.

| State bag     | Valor                                                                                             |
| ------------- | ------------------------------------------------------------------------------------------------- |
| `gymStats`    | `{ strength, agility }` con los niveles actuales.                                                 |
| `gymExercise` | `{ zoneId, eqId, exerciseId, anchor, coords, businessId, helper }` mientras entrena; `nil` si no. |
| `gymHelping`  | `{ owner, eqId }` mientras ayuda; `nil` si no.                                                    |

```lua
local levels = Player(source).state.gymStats
```

## Exports de compatibilidad

Para recursos escritos contra un sistema de habilidades con `Strength`, `Stamina` y `Running`. Están disponibles como `exports.cuxial_gym` y como `exports.CuxialGym`.

| Export                                    | Qué hace                                                                                                                                           |
| ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AddSkillToPlayer(source, skill, amount)` | Suma `amount` puntos a una habilidad. Cada punto vale `compat.xpPerPoint` de XP de la estadística asignada en `compat.skills`. Devuelve `boolean`. |
| `GetPlayerSkills(source)`                 | Devuelve `{ Strength, Stamina, Running }` de 0 a 100, escalado desde los niveles de las estadísticas.                                              |
| `MaxAllSkills`                            | Función de item para `ox_inventory`: al usar el item, las dos estadísticas suben al nivel máximo.                                                  |

```lua
exports.CuxialGym:AddSkillToPlayer(source, 'Strength', 2)
```

Un item de ejemplo que usa `MaxAllSkills`:

{% code title="ox\_inventory/data/items.lua" %}

```lua
['gym_master_pill'] = {
    label = 'Master pill',
    weight = 10,
    consume = 1,
    server = { export = 'cuxial_gym.MaxAllSkills' },
},
```

{% endcode %}


---

# 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/ocio/cuxial-gym/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.
