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

# Exports & events

Public API of Cuxial Gym: exports, server events, hooks and state bags for other resources.

Read a player's stats, grant memberships, move business money or react to a workout from your own resources.

In every export, `cid` is the character identifier your framework uses, and business and zone ids are the numbers shown in the staff editors.

## Server exports · players

### GetStats

Returns the levels and XP of an online player.

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

**Returns:** `{ strength = { level, xp }, agility = { level, xp } }`, or `nil` if the player is not loaded.

```lua
local stats = exports.cuxial_gym:GetStats(source)
if stats and stats.strength.level >= 25 then
    -- strong enough to carry the crate
end
```

### AddStatXp

Adds XP to one stat of an online player.

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

| Parameter | Type   | Description                        |
| --------- | ------ | ---------------------------------- |
| `source`  | number | Server id of the player.           |
| `stat`    | string | `'strength'` or `'agility'`.       |
| `xp`      | number | XP to add. Must be greater than 0. |

**Returns:** `number`. Levels gained; `0` when nothing was added.

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

### GetFatigue

Returns the current fatigue of each body part of an online player.

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

**Returns:** `table<string, number>` keyed by body part (`chest`, `shoulders`, `back`, `arms`, `abs`, `thighs`, `calves`), or `nil`.

### GrantMembership

Gives a player a pass for a zone. Works with offline characters.

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

| Parameter   | Type             | Description                                   |
| ----------- | ---------------- | --------------------------------------------- |
| `target`    | number \| string | Server id, or `cid` of the character.         |
| `zoneId`    | number           | Zone the pass is for.                         |
| `days`      | number           | Duration, from 1 to 3650.                     |
| `packageId` | number           | Optional. Package the pass is recorded under. |

**Returns:** `boolean`, and on failure a reason: `'not_ready'`, `'unknown_zone'`, `'invalid_days'` or `'player_not_found'`.

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

### RevokeMembership

Removes a player's pass for a zone.

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

**Returns:** `boolean`, and on failure `'not_ready'`, `'unknown_zone'`, `'player_not_found'` or `'no_membership'`.

### GetMembership

Returns the active pass of a player in a zone.

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

**Returns:** `{ zoneId, businessId, packageId, expiresAt, purchasedAt, secondsLeft }`, or `nil` when there is no active pass. Times are Unix timestamps in seconds.

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

## Server exports · businesses

### Reading

| Export                             | Returns                                                                                                    |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `GetBusinessInfo(id)`              | `{ id, name, type, logo, color, funds, level, xp, maxXp, ownerName }`, or `nil`. `type` is always `'gym'`. |
| `GetBusiness(id)`                  | Full snapshot of the business, as the business panel shows it, or `nil`.                                   |
| `GetAllBusinesses()`               | Table of snapshots keyed by business id.                                                                   |
| `GetBusinessesByType(kind)`        | The same table when `kind` is `'gym'`; empty otherwise.                                                    |
| `GetBusinessFunds(id)`             | `number`. `0` if the business does not exist.                                                              |
| `GetBusinessLevel(id)`             | `{ level, xp, maxXp }`, or `nil`.                                                                          |
| `GetEmployees(id)`                 | List of `{ identifier, name, avatar, grade, minutesWorked, repsHelped, pendingBonus, totalBonus }`.        |
| `GetEmployeeCount(id)`             | `number`.                                                                                                  |
| `GetWarehouse(id)`                 | List of `{ name, stock, enabled }`.                                                                        |
| `GetWarehouseStock(id, item)`      | `number`.                                                                                                  |
| `GetBusinessInteractionPoints(id)` | List of `{ id, scope, ownerId, mode, model, coords, rotation, radius, actions }`.                          |
| `GetBusinessMetadata(id)`          | Copy of the business metadata, or `nil`.                                                                   |

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

### Members

| Export                               | Returns                                                     |
| ------------------------------------ | ----------------------------------------------------------- |
| `GetPlayerBusinesses(cid)`           | `number[]`. Ids of the businesses the character belongs to. |
| `IsPlayerInBusiness(cid, id)`        | `boolean`.                                                  |
| `IsPlayerOwner(cid, id)`             | `boolean`.                                                  |
| `GetPlayerRole(cid, id)`             | `string`. Identifier of the grade, or `nil`.                |
| `HasPermission(cid, id, permission)` | `boolean`.                                                  |

`permission` is one of `'manage_company'`, `'manage_employees'`, `'manage_permissions'`, `'manage_warehouse'`, `'manage_finances'`, `'manage_sellers'`, `'pay_bonuses'`, `'transfer_ownership'`, or its position in that list (1 to 8).

```lua
if exports.cuxial_gym:HasPermission(cid, 1, 'manage_finances') then
    -- can touch the money
end
```

### HirePlayer

Adds a character to a business without an offer.

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

| Parameter | Type   | Description                                          |
| --------- | ------ | ---------------------------------------------------- |
| `id`      | number | Business id.                                         |
| `cid`     | string | Character identifier.                                |
| `name`    | string | Name shown in the employee list.                     |
| `grade`   | string | Optional. Grade identifier; `'employee'` by default. |

**Returns:** `boolean`. `false` when the character already works there, the grade does not exist or the business has no free employee slot.

### FirePlayer

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

**Returns:** `boolean`. `false` when the character is not in the business.

### Money

| Export                                          | What it does                                                                                          |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `AddBusinessIncome(id, amount, reason, source)` | Records an income. It counts for taxes, business XP and missions. `reason` and `source` are optional. |
| `DepositToBusiness(id, amount, source)`         | Adds funds as a deposit. `source` is optional.                                                        |
| `WithdrawFromBusiness(id, amount, source)`      | Takes funds out. Fails when there is not enough.                                                      |

All three return `boolean`. `amount` is a whole number greater than 0. `source`, when given, is the player recorded as the author of the transaction.

{% hint style="warning" %}
These exports only change the business funds. They do not give or take money from any player: do that in your own code.
{% endhint %}

```lua
if exports.cuxial_gym:AddBusinessIncome(1, 2500, 'Private class', source) then
    -- charge the player here
end
```

### Stock, XP and metadata

| Export                                   | What it does                                    |
| ---------------------------------------- | ----------------------------------------------- |
| `AddWarehouseStock(id, item, amount)`    | Adds units of an item to the warehouse.         |
| `RemoveWarehouseStock(id, item, amount)` | Removes units. Fails when there are not enough. |
| `AddBusinessXP(id, amount)`              | Adds business XP.                               |
| `SetBusinessMetadata(id, key, value)`    | Sets one metadata key. `nil` clears it.         |

All four return `boolean`.

`SetBusinessMetadata` accepts only these keys, with these types: `workArea` (table), `workerLevels` (table), `membership` (table), `incomeTax` (number), `xpRate` (number), `helperRepsPerMinigame` (number) and `callCooldownUntil` (number).

## Client exports

| Export                   | Returns       | What it does                                                                                  |
| ------------------------ | ------------- | --------------------------------------------------------------------------------------------- |
| `IsExercising()`         | boolean       | Whether the local player is in a set.                                                         |
| `IsHelping()`            | boolean       | Whether the local player is spotting someone.                                                 |
| `OpenGymPanel(zoneId)`   | boolean       | Opens the gym panel of a zone. The player must be near a gym menu point of that zone.         |
| `OpenBusinessPanel(id)`  | boolean       | Opens the business panel. Without `id`, opens the player's business or a list to choose from. |
| `CloseBusinessPanel()`   | boolean       | Closes it.                                                                                    |
| `IsBusinessPanelOpen()`  | boolean       | Whether the business panel is open.                                                           |
| `GetCurrentBusinessId()` | number \| nil | Id of the business shown in the open panel.                                                   |
| `IsInBusiness(id)`       | boolean       | Whether the local player is staff of that business.                                           |

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

### UseGizmo

Lets the player move and rotate an entity with the placement gizmo, and waits until they confirm or cancel.

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

| Parameter     | Type    | Description                                            |
| ------------- | ------- | ------------------------------------------------------ |
| `entity`      | number  | Entity to place.                                       |
| `opts.camera` | boolean | Optional. `true` uses the placement camera of `gizmo`. |

**Returns:** `{ position = vector3, rotation = vector3, cancelled = boolean }`, or `nil` if the entity does not exist.

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

## Server events

Triggered on the server only. Listen with `AddEventHandler`.

| Event                         | Parameters                | When                                                                                       |
| ----------------------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
| `cuxial_gym:exerciseStarted`  | `source, ctx`             | A player starts a set.                                                                     |
| `cuxial_gym:exerciseFinished` | `source, ctx`             | A set ends, for any reason.                                                                |
| `cuxial_gym:statLevelUp`      | `source, data`            | A player gains a level. `data` is `{ strength, agility, gained = { strength, agility } }`. |
| `cuxial_gym:stockChanged`     | `businessId, item, stock` | The stock of an item changes in a warehouse.                                               |

`ctx` fields:

| Field        | Type            | Description                                                                                                                                                                  |
| ------------ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `exerciseId` | string          | Exercise, as in `data/exercises.lua`.                                                                                                                                        |
| `eqId`       | string          | Piece of equipment.                                                                                                                                                          |
| `zoneId`     | number \| false | Zone; `false` on a global model.                                                                                                                                             |
| `zoneName`   | string \| false | Name of the zone.                                                                                                                                                            |
| `businessId` | number \| false | Business that owns the zone.                                                                                                                                                 |
| `bodyPart`   | string          | Main body part trained.                                                                                                                                                      |
| `maxReps`    | number          | Rep limit of the exercise; `0` is unlimited.                                                                                                                                 |
| `global`     | boolean         | `true` on a map prop outside any zone.                                                                                                                                       |
| `reps`       | number          | Reps done. Only when finished.                                                                                                                                               |
| `freeReps`   | number          | Extra reps earned. Only when finished.                                                                                                                                       |
| `levels`     | table           | Levels gained: `{ strength, agility }`. Only when finished.                                                                                                                  |
| `durationMs` | number          | Length of the set. Only when finished.                                                                                                                                       |
| `reason`     | string          | Why it ended: `esc_cancel`, `minigame_fail`, `max_reps`, `fatigue_overload`, `too_far`, `config_stop`, `anim_failed`, `server_ended` or `resource_stop`. Only when finished. |

```lua
AddEventHandler('cuxial_gym:exerciseFinished', function(source, ctx)
    if ctx.reps >= 20 then
        -- reward a long set
    end
end)
```

## Hooks

Two files are left open for your own code: `server/hooks.lua` and `client/hooks.lua`. Fill in the body of each function; an error inside a hook is caught and printed.

### Server

| Hook                                    | Called                                 | Return                                                                      |
| --------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------- |
| `hooks.onPlayerLoaded(src, cid, stats)` | The gym data of a character is loaded. | None                                                                        |
| `hooks.onStatLevelUp(src, cid, stats)`  | The player gains a level.              | None                                                                        |
| `hooks.canStartExercise(src, ctx)`      | Before a set starts.                   | `false` blocks it. A second value sets the locale key of the message shown. |
| `hooks.onExerciseStarted(src, ctx)`     | A set starts.                          | None                                                                        |
| `hooks.onExerciseFinished(src, ctx)`    | A set ends.                            | None                                                                        |

`stats` is `{ strength = { level, xp }, agility = { level, xp } }`. `ctx` is the same table as in the events.

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

```lua
function hooks.canStartExercise(src, ctx)
    -- no training outside the main world
    if GetPlayerRoutingBucket(src) ~= 0 then
        return false
    end
    return true
end
```

{% endcode %}

### Client

| Hook                        | Called                              | Return                |
| --------------------------- | ----------------------------------- | --------------------- |
| `hooks.onExerciseRep(data)` | After each rep of the local player. | `false` ends the set. |

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

## State bags

Set by the server on each player and replicated to clients. They are read only: a change made from a client is reverted.

| State bag     | Value                                                                                               |
| ------------- | --------------------------------------------------------------------------------------------------- |
| `gymStats`    | `{ strength, agility }` with the current levels.                                                    |
| `gymExercise` | `{ zoneId, eqId, exerciseId, anchor, coords, businessId, helper }` while training; `nil` otherwise. |
| `gymHelping`  | `{ owner, eqId }` while spotting; `nil` otherwise.                                                  |

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

## Compatibility exports

For resources written against a skill system with `Strength`, `Stamina` and `Running`. They are available both as `exports.cuxial_gym` and as `exports.CuxialGym`.

| Export                                    | What it does                                                                                                                          |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `AddSkillToPlayer(source, skill, amount)` | Adds `amount` points to a skill. Each point is worth `compat.xpPerPoint` XP of the stat mapped in `compat.skills`. Returns `boolean`. |
| `GetPlayerSkills(source)`                 | Returns `{ Strength, Stamina, Running }` from 0 to 100, scaled from the stat levels.                                                  |
| `MaxAllSkills`                            | Item callback for `ox_inventory`: using the item raises both stats to the level cap.                                                  |

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

An example item that uses `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/leisure/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.
