> For the complete documentation index, see [llms.txt](https://safky.gitbook.io/product-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://safky.gitbook.io/product-docs/scripts/s_arcade_pvp.md).

# ARCADE PVP

PvP arenas with lobbies. Team Deathmatch, Free For All and Gun Game.

PvP arenas with lobbies: Team Deathmatch, Free For All and Gun Game. Each match runs in its own routing bucket.

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-f2ca589a182bf196ed2e8092908e67397191c9b5%2Farcade-hud.jpg?alt=media" alt="The in-game HUD in Team Deathmatch: team score, kill feed with a headshot, the top 5 leaderboard and the player&#x27;s K/D."><figcaption><p>The in-game HUD in Team Deathmatch: team score, kill feed with a headshot, the top 5 leaderboard and the player's K/D.</p></figcaption></figure>

***

## ✅ Requirements

|                  |                                                                                                               |
| ---------------- | ------------------------------------------------------------------------------------------------------------- |
| **Frameworks**   | Any, or none                                                                                                  |
| **Dependencies** | [ox\_lib](https://github.com/overextended/ox_lib), OneSync                                                    |
| **Inventories**  | ox\_inventory, qb, ps, lj are locked during a match (detected automatically)                                  |
| **Medical**      | qbx\_medical, qb-ambulancejob, esx\_ambulancejob are released after each arena death (detected automatically) |

***

## 📥 Installation

```
ensure ox_lib
ensure s_arcade_pvp
```

No database and no items. Your inventory and death script need no edits.

***

## ⚙️ Configuration

Everything in `config/` is open. Every option in `config/config.lua` is commented in the file; this page covers the ones that need more than a comment.

### Maps

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-f65b7487e8fca39f0a473e0bd41029c26d2ac54d%2Farcade-create.jpg?alt=media" alt="The create room menu: game modes and the map grid with the preview images."><figcaption><p>The create room menu: game modes and the map grid with the preview images.</p></figcaption></figure>

```lua
['my_map'] = {
    name = 'My Map',
    bounds = {
        vec3(100.0, 200.0, 30.0),
        vec3(180.0, 200.0, 30.0),
        vec3(180.0, 280.0, 30.0),
        vec3(100.0, 280.0, 30.0),
    },
    spawnPoints = {
        [1] = { vec4(110.0, 210.0, 30.0, 45.0) },  -- team 1
        [2] = { vec4(170.0, 270.0, 30.0, 225.0) }, -- team 2
    },
    respawnPoints = {
        vec4(120.0, 240.0, 30.0, 90.0),
        vec4(160.0, 240.0, 30.0, 270.0),
    },
},
```

* `bounds` is the play area as a polygon, at least three points. The height limit runs from 5 m below the lowest point to 40 m above the highest. Leaving the area counts as a death and respawns the player.
* `spawnPoints` are used once, when the match starts. In Free For All and Gun Game players start on the `respawnPoints` in turn.
* In Team Deathmatch each team respawns on the `respawnPoints` closer to its own spawn. `RespawnSafeRadius` then picks one with no enemy that close.
* The preview image is `web/images/maps/<key>.webp`. Only `.webp` is loaded.

### Menu locations

Players return to the location where the lobby was created. Creating or joining a lobby is refused more than 15 m outside the marker radius.

### Loadouts and Gun Game

The loadout key is what the host picks in the menu. `ammo` is the ammo given on every spawn.

Gun Game ignores loadouts and walks through `GunGameWeapons` in order, one weapon per kill. The first player to get a kill with the last weapon wins.

### Game modes

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-b1ad65b8b7e209638e2c5c7c4455ce38f1cfd286%2Farcade-lobby.jpg?alt=media" alt="A Team Deathmatch lobby with custom team names and colours, waiting for the host to start."><figcaption><p>A Team Deathmatch lobby with custom team names and colours, waiting for the host to start.</p></figcaption></figure>

`maxPlayers` caps the stepper in the create menu. The names and descriptions shown to players come from the locale (`mode_tdm`, `mode_tdm_desc`, and so on); the `name` in the config is used only when the locale has no entry.

### Locales

`Locale` picks a file in `config/locales/`. `en` and `pt-pt` are included. A translation missing keys falls back to English.

### Inventory and death scripts

`config/framework/cl-framework.lua` decides what the arena does with your inventory and your medical script.

| Function                | What it does                                                                              |
| ----------------------- | ----------------------------------------------------------------------------------------- |
| `LockInventory(locked)` | Blocks the inventory during a match and stops ox\_inventory from taking the arena weapons |
| `RestoreWeapons()`      | ESX without an item inventory: gives the player's own weapons back on exit                |
| `IsDowned()`            | True while the medical script has the player dead or in last stand                        |
| `Revive()`              | Clears the medical script's death state after each arena death                            |

For a medical script that is not detected, fill `IsDowned` and `Revive` with its own export or event. Without them the player still respawns, but that script may keep its own death state.

{% hint style="info" %}
qb-ambulancejob sends an EMS alert every time a player goes into last stand, arena included. To silence it, skip the `hospital:server:ambulanceAlert` call in its `client/laststand.lua` when `LocalPlayer.state.inPvpArena` is true.
{% endhint %}

***

## 💬 Commands

| Command    | Who  | What it does                  |
| ---------- | ---- | ----------------------------- |
| `/endgame` | Host | Ends the match with no winner |

The name is `EndGameCommand` in `config/config.lua`. If the host disconnects mid-match, another player becomes host.

***

## 🔌 Integration

### State bag

`inPvpArena` is set by the server and replicated. It stays true until the player is back at the arcade, so a death during the end of a match is still an arena death. Read it to switch off other systems during a match:

```lua
-- server
if Player(source).state.inPvpArena then return end

-- client
if LocalPlayer.state.inPvpArena then return end
```

### Kills and custom damage scripts

A kill counts when the server saw the attacker hit the victim in the 10 seconds before the death. Scripts that cancel `weaponDamageEvent` to apply damage their own way (anti-tank, damage tables) keep working: the kill goes to the last player who hit the victim.

### Hooks

| File               | Function                         | What it does                                                  |
| ------------------ | -------------------------------- | ------------------------------------------------------------- |
| `cl-functions.lua` | `Notify(msg, notifyType)`        | Notifications. Uses ox\_lib by default                        |
| `cl-functions.lua` | `OnArenaEnter()`                 | Runs when the player enters a match, after the inventory lock |
| `cl-functions.lua` | `OnArenaExit()`                  | Runs on any exit: end, cancel, disconnect or resource restart |
| `sv-functions.lua` | `OnMatchEnd(lobbyName, results)` | Runs once per finished match. Use it for rewards or logs      |

<figure><img src="https://3876653196-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FwqRMsiuM4yL1gQLB2nlA%2Fuploads%2Fgit-blob-5d161aac5ab216a51b6ce6a0ae2339df0a04ad04%2Farcade-results.jpg?alt=media" alt="The results screen at the end of a match: the winning team and every player&#x27;s kills, deaths and K/D."><figcaption><p>The results screen at the end of a match: the winning team and every player's kills, deaths and K/D.</p></figcaption></figure>

`results.winnerType` is `'team'` (`winner` is `1` or `2`), `'player'` (`winner` is the name) or `'manual'`. `results.players` is a list of `{ id, name, kills, deaths, team }` sorted by kills.
