> 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-multicharacter-v2-rsg/cas-multicharacter-v2-rsg.md).

# CAS Multicharacter V2 (RSG)

A fully custom, React-based multicharacter selection system for **RedM** on the **RSG** framework (`cas-multicharacter`). It features cinematic character previews with walking animations, horse mounts, per-slot camera angles and a Tebex-integrated slot unlock system.

## Highlights

* **React-based NUI** — modern, responsive UI (React + TypeScript + Tailwind)
* **Cinematic previews** — per-slot world coords, walking paths, idle animations and camera angles
* **Horse mount support** — characters can spawn mounted during selection
* **Full skin loading** via `rsg-appearance`
* **Slot lock/unlock** behind Tebex purchases with transaction-ID verification
* **Multi-SQL** — oxmysql, ghmattimysql or mysql-async
* **Starter items & horse** for new characters
* **Logout & re-select** without reconnecting
* **Third-party integration** — open selection from other resources via event

## At a glance

|               |                                      |
| ------------- | ------------------------------------ |
| Framework     | RSG                                  |
| Resource name | `cas-multicharacter`                 |
| Database      | oxmysql / ghmattimysql / mysql-async |

## Dependencies

| Resource                                   | Required     |
| ------------------------------------------ | ------------ |
| `rsg-core`                                 | Yes          |
| `rsg-appearance`                           | Yes          |
| `rsg-spawn`                                | Yes          |
| `weathersync`                              | Yes          |
| `oxmysql` / `ghmattimysql` / `mysql-async` | One of these |

## Installation

1. Place `cas-multicharacter` in your `resources` folder.
2. Add `ensure cas-multicharacter` to `server.cfg` (after `rsg-core` and `rsg-appearance`).
3. Import `tebexintegratersg.sql` — it creates `cas_slots` and `cas_multichartebexids`.
4. If you are replacing the default, stop it:

```cfg
#ensure rsg-multicharacter
ensure cas-multicharacter
```

## Configuration

### Server — `config/server_config.lua`

```lua
Config.StarterHorse = true
Config.StarterHorseModel = 'a_c_horse_mp_mangy_backup'
Config.StarterHorseStable = 'valentine'
Config.StarterHorseName = 'Starter Horse'
```

### Client — `config/client_config.lua`

```lua
Config.TebexLink = "https://your-store.tebex.io"
Config.discordLink = "https://discord.gg/your-server"

SkinFunction = function(skinData, ped, clothesData)
    exports['rsg-appearance']:ApplySkinMultiChar(skinData, ped, clothesData)
end
```

### Character slots — `config/shared_config.lua`

Each slot is defined in `Config.Characters`:

```lua
Config.Characters = {
    [1] = {
        camSettings = { fov = 20.0, offsetX = 8.0, offsetY = 2.5, offsetZ = 1 },
        randomPeds = { "cs_unidusterjail_01", "cs_mp_cripps" },
        scenarioData = {
            startCoord = vector4(-799.465, -1369.105, 43.540, 4.599),
            walkTo = vector4(-802.178, -1253.551, 43.457, 351.132),
            horseSettings = { horseHash = `A_C_HORSE_AMERICANSTANDARDBRED_BLACK`, horseCoord = vector4(-799.946, -1365.228, 42.558, 0.541) },
        },
        reactData = { locked = false, empty = true },
    },
}
```

| Property                     | Description                       |
| ---------------------------- | --------------------------------- |
| `camSettings.fov`            | Camera zoom (lower = closer)      |
| `camSettings.offsetX/Y/Z`    | Camera offset from the ped        |
| `scenarioData.startCoord`    | Where the ped spawns              |
| `scenarioData.walkTo`        | Where the ped walks to            |
| `scenarioData.horseSettings` | Optional horse mount              |
| `scenarioData.animation`     | Optional idle animation           |
| `reactData.locked`           | Requires a Tebex unlock           |
| `randomPeds`                 | Ped models for empty/locked slots |

## Tebex slot unlock

1. An admin registers a transaction ID via console/RCON: `purchaseslot <tebex_transaction_id>`.
2. The player enters the ID in the NUI to unlock a locked slot.
3. The system verifies the ID is unused, marks it used, and unlocks the slot for that player's license.

```sql
CREATE TABLE IF NOT EXISTS `cas_multichartebexids` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `tebexId` VARCHAR(255) NOT NULL, `used` TINYINT(1) DEFAULT 0 );
CREATE TABLE IF NOT EXISTS `cas_slots` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `license` VARCHAR(255) NOT NULL, `unlockedSlots` LONGTEXT DEFAULT '[]' );
```

## Events

**Client:** `cas-multicharacter:LoadFromThirdParty` (opens selection), `cas-multicharacter:client:logout`.

**Server:** `cas-multicharacter:server:logout`, `cas-multicharacter:LoadCharacter`, `cas-multicharacter:CreateNewChar`.

Open selection from another resource:

```lua
TriggerEvent("cas-multicharacter:LoadFromThirdParty")
```

## Commands

| Command             | Permission   | Description                     |
| ------------------- | ------------ | ------------------------------- |
| `logout`            | Everyone     | Return to character selection   |
| `purchaseslot <id>` | Console/RCON | Register a Tebex transaction ID |

## Web UI

The NUI is React + TypeScript + Vite + Tailwind. Build with:

```bash
cd web
npm install
npm run build
```

The output goes to `web/build/`, served by the fxmanifest.

## Troubleshooting

* **Characters invisible/naked** — ensure `rsg-appearance` is running and `ApplySkinMultiChar` is available; check the `playerskins` data.
* **Selection doesn't open on join** — ensure the resource loads after `rsg-core`; check that `weathersync` is running.
* **NUI frozen** — check the browser console (F8); all callbacks must call `cb()`.
* **Slot unlock fails** — verify the ID exists in `cas_multichartebexids` with `used = 0`, and that `cas_slots` exists.
* **Logout not working** — ensure no other resource overrides the `logout` command.

## Support

Join the [CAS Discord](https://discord.gg/X8bTK9Stwk) or use the support channel on your product page.
