> For the complete documentation index, see [llms.txt](https://code-after-sex.gitbook.io/script-documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://code-after-sex.gitbook.io/script-documentation/cas-wagon-livery-studio/developers.md).

# For developers

CAS Wagon Livery Studio for RedM developers: the GlobalState bag that says which livery each player's wagon carries, the events, the livery:mount report, and how livery slots and props are named.

The Studio has no exports for other scripts to call. What each player's wagon carries is public state that any script can read, and the slot and prop names are fixed, so you can check for them.

{% hint style="info" %}
The server exports `lvBridgeGetPlayer`, `lvBridgeRemoveMoney`, `lvBridgeIsAdmin` and `lvConfig` are the Studio's own bridge between its Lua and JavaScript halves. They are not an API: do not call them, they can change in any version.
{% endhint %}

## The state bag

The server keeps one bag per player in `GlobalState`, under `lvLivery:<serverId>`. Only the server writes it. It is `nil` while the player's wagon carries nothing (or the player has no wagon out).

```lua
GlobalState["lvLivery:12"] = {
    wagon = { net = 5678, designId = 12, model = "wagon02x", prop = "cas_lv_wagon02x_005", slot = "005" },
}
```

* `net` is the wagon's network id, `prop` the livery prop the client attaches, `slot` the livery slot it draws from.
* Only a livery the character may still use is in the bag: owned, their job's, or a gift.

Reading it on a client:

```lua
local bag = GlobalState["lvLivery:" .. GetPlayerServerId(PlayerId())]
if bag and bag.wagon then
    print(("my %s carries design %d"):format(bag.wagon.model, bag.wagon.designId))
end

AddStateBagChangeHandler(nil, "global", function(_, key, value)
    if key:sub(1, 9) == "lvLivery:" then
        -- a player's livery changed (value may be nil)
    end
end)
```

Each client draws the liveries itself from these bags, within `Config.DrawDistance`: the props are local to each client, never networked, and have no collision.

## livery:mount

Each player's client tells the server which wagon is theirs, through the Studio's request channel (the event `cas-liverystudio:rpc`, request name `livery:mount`):

```lua
{ wagon = { net = 5678, model = "wagon02x" } }
```

* Sent when it changes (checked every 2 seconds), and again after a character is chosen (`vorp:SelectedCharacter`, `RSGCore:Client:OnPlayerLoaded`) or when the server asks (`cas-liverystudio:mine:refresh`).
* With CAS Stables the wagon comes from its client export `GetWagon()`; without it, the livery wagon the player drives.
* The wagon model must be one the Studio has livery props for (`data/wagons.json`).
* The server answers with the new bag and writes it to `GlobalState`.

This is the Studio's own client talking to its server. Another script should not send it.

## Where a livery sits

The book stores what is on what in `cas_livery_applied`: one row per character, holder and part.

* Holder `w:<model>`: the player's wagons of that model.
* Part `livery`: one design per holder.

## Events

All start with `cas-liverystudio:`. They are internal; listed so you know what you see in logs and event tools.

* `rpc`, `rpcpart` (client to server): a request (`reqId, name, payload`), big ones in pieces.
* `rpc:reply`, `rpc:replypart` (server to client): the answer, big ones in pieces.
* `upload` (client to server): the pieces of an export (staff only).
* `export:done` (server to client): the export was built, or why not.
* `open` (server to client): open the studio. `notify` (server to client): a short message by code.
* `mine:refresh` (server to client): what the player owns changed (a design deleted, a gift taken back); the client reports its wagon again.

Requests: staff `studio:open`, `designs:list`, `designs:get`, `designs:save`, `designs:thumbs`, `designs:delete`, `handout:save`, `players:list`, `export:begin`, `ai:generate`; players `book:open`, `book:buy`, `book:apply`, `book:remove`, `livery:mount`. A refusal is a code such as `not_allowed`, `no_money`, `not_for_sale`, `not_here`, `no_wagon`, `wrong_wagon`, `no_free_slot`; its words are `error.<code>` in the language files.

## Slots and props

**Livery slots:** `cas_lv_s001` to `cas_lv_s032` (`data/sheets.json`). Each is one texture dictionary, `stream/designs/cas_lv_sNNN.ytd`, with one 1024 x 1024 BC3 texture, `cas_lv_livery_abal`. A prop finds its texture only through its archetype, so every slot has its own props, in `stream/props/` with their archetypes in `cas_lv_props.ytyp`:

* **Wagon props:** `cas_lv_<model>_NNN`, for example `cas_lv_wagon02x_005`: one per wagon model and livery slot, 608 in all. Attached to the wagon at its origin, plus the `Config.Wagons` offset.

{% hint style="warning" %}
A resource that streams a file with one of these names takes that slot: the Studio leaves it unused and the console says `N slot(s) are streamed by another resource and stay unused`.
{% endhint %}

## Wagon models

`data/wagons.json` lists the supported models with their label, prop and livery areas (`left`, `right`, `rear`, each with its rectangle on the sheet and its size in metres). A modded version of one of these models can be nudged with `Config.Wagons`. A new wagon model needs its own props, which are made offline with the Studio's tools; ask CAS support.
