# Configuration

Everything lives in `shared/config.lua`. Restart the resource after a change.

## General

| Option | Default | What it does |
| --- | --- | --- |
| `Config.Locale` | `'pl'` | Language, a file name from `shared/locales/` |
| `Config.Debug` | `false` | Prints selections, combat logs and failing checks to the console |
| `Config.ServerName` | `'KOJA ROLEPLAY'` | Shown in the panel header |
| `Config.Theme.accent` | `'#FF9B00'` | Accent colour of buttons, pins, the beacon, the route and the glow |
| `Config.Theme.background` | `'#121212'` | Panel background, mixed with a warm brown like Koja Appearance |
| `Config.Fallback` | Legion Square | Where a player spawns when no location is available |

## Opening

```lua title="shared/config.lua"
Config.Open = {
    OnPlayerLoaded = true,
    Delay = 250,
    CanCancel = false,
    ShowLocked = true,
    RememberChoice = true,
}
Config.QbSpawnCompat = true
```

| Option | What it does |
| --- | --- |
| `OnPlayerLoaded` | ESX: open on `esx:playerLoaded`. Skipped while Koja Multicharacter runs |
| `Delay` | ms to wait after `esx:playerLoaded` before opening |
| `CanCancel` | Default for ESC / the exit button. Every `Open` call can override it with `canCancel`. Never allowed while a restriction is active |
| `ShowLocked` | `true` shows locked spots with their lock label, `false` hides them |
| `RememberChoice` | Preselect the location the player picked last time |
| `Config.QbSpawnCompat` | Listen to the `qb-spawn` events from qb-multicharacter |

## Admin

```lua title="shared/config.lua"
Config.Command = { Enabled = true, Name = 'spawnselector', Editor = 'spawneditor', Event = 'spawnevent' }
Config.Admin = { Groups = { 'admin', 'superadmin', 'god' }, Ace = 'command' }
```

The check goes through `KOJA.Server.IsAdmin`; change it in `ServerEditable.IsAdmin`. See [Admin tools](/new/spawn-selector/admin-tools).

## Last location

```lua title="shared/config.lua"
Config.LastLocation = {
    Enabled = true,
    SaveInterval = 60,
    FrameworkFallback = true,
    IgnoreBuckets = true,
}
```

| Option | What it does |
| --- | --- |
| `Enabled` | Show "Last location" and save positions |
| `SaveInterval` | Seconds between saves of every spawned player (minimum 10). Positions are also saved on logout, disconnect and resource stop |
| `FrameworkFallback` | No saved row yet: use `users.position` (ESX) or `players.position` (QBCore) |
| `IgnoreBuckets` | Do not save players in a routing bucket other than 0 (interiors, instances) |

## Anti combat log, death and jail

`Config.AntiCombatLog`, `Config.Death` and `Config.Jail` are explained with the API on [Anti combat log & restrictions](/new/spawn-selector/anti-combat-log).

## New characters

```lua title="shared/config.lua"
Config.NewCharacter = {
    UseSelector = false,
    Spawns = {
        { id = 'start_airport', label = 'Los Santos International', description = 'Arrivals', category = 'start', coords = vec4(-1037.74, -2737.89, 20.17, 327.9) },
    },
}
```

`UseSelector = false` leaves new ESX characters to your character creator. When a new character opens the selector (`isNew = true`), only these spawns are listed.

## Camera flight

```lua title="shared/config.lua"
Config.Cinematic = {
    Enabled = true,
    Height = 650.0,
    TopFov = 45.0,
    HoldTime = 900,
    DescendTime = 3200,
    ApproachDistance = 22.0,
    ApproachHeight = 14.0,
    SettleTime = 1800,
    FinalDistance = 3.4,
    FinalHeight = 0.85,
    BlendOut = 1100,
    LoadTimeout = 6000,
}
```

| Option | What it does |
| --- | --- |
| `Enabled` | `false` = a plain fade and teleport |
| `Height`, `TopFov`, `HoldTime` | The top-down shot: metres above the spot, field of view, how long it holds (ms) |
| `DescendTime` | ms of the descent to the street |
| `ApproachDistance`, `ApproachHeight` | Where the descent ends: metres behind and above the ped |
| `SettleTime`, `FinalDistance`, `FinalHeight` | The last move to an over-the-shoulder shot |
| `BlendOut` | ms of the blend into the gameplay camera |
| `LoadTimeout` | Longest wait (ms) for collision and streaming at the spot |

## Map

```lua title="shared/config.lua"
Config.Map = {
    FocusDistance = 2600,
    OnlineDetail = true,
    ShowWorld = true,
    BlipImageUrl = 'https://docs.fivem.net/blips/%s.png',
    GameBlips = {
        Enabled = true,
        Sprites = { [60] = 'radar_police_station', [61] = 'radar_hospital' },
    },
}
```

| Option | What it does |
| --- | --- |
| `FocusDistance` | Camera distance in metres when a location is selected |
| `OnlineDetail` | Up close, stream the sharper zoom 7 tiles from the Rockstar map CDN. Offline the shipped zoom 6 tiles are used |
| `ShowWorld` | Time of day, weather, rain and snow on the map |
| `BlipImageUrl` | Where blip icons come from, `%s` = the blip name |
| `GameBlips` | Game blips shown on the map: sprite id = blip image name |

```lua title="shared/config.lua"
Config.Roads = { enabled = true, file = 'data/roads.json', cellSize = 150.0, maxSnapRings = 12, zWeight = 0.5, speedFactor = { [0] = 0.6, 0.9, 1.15, 1.45 }, maxExpansions = 20000, cacheSize = 64 }
```

The road graph for the route line. `enabled = false` turns the route off; the other values are the koja-dispatch router settings.

## Live preview

```lua title="shared/config.lua"
Config.LivePreview = { Enabled = true, Distance = 9.0, Height = 3.2, Fov = 45.0, Orbit = 6.0 }
```

The camera circling the selected spot in the card: metres behind, metres up, field of view and degrees per second.

## Sounds

```lua title="shared/config.lua"
Config.Sound = { Enabled = true, Volume = 0.45, Ambient = true, Music = nil }
```

`Music` takes any URL the NUI can play (`https://...` or `nui://your-resource/music.mp3`) and replaces the synthesised ambient.

## Housing

```lua title="shared/config.lua"
Config.Housing = {
    ['qb-houses'] = 'auto',
    ['qs-housing'] = 'auto',
    ['ps-housing'] = 'auto',
    ['esx_property'] = 'auto',
    ['nolag_properties'] = 'auto',
    ['bcs_housing'] = 'auto',
}
```

`'auto'` lists owned properties when that resource is started, `true` always tries, `false` turns the provider off.

## Stats

```lua title="shared/config.lua"
Config.Stats = { Enabled = true, KeepDays = 90 }
```

## Categories

```lua title="shared/config.lua"
Config.Categories = {
    { id = 'event', icon = 'star', order = 0 },
    { id = 'last', icon = 'footprints', order = 1 },
    { id = 'start', icon = 'plane', order = 2 },
    { id = 'property', icon = 'home', order = 3 },
    { id = 'job', icon = 'briefcase', order = 4 },
    { id = 'gang', icon = 'skull', order = 5 },
    { id = 'city', icon = 'map-pin', order = 6 },
}
```

The label comes from the locale key `category_<id>`, or set `label` yourself. Other resources can add categories with [`RegisterCategory`](/new/spawn-selector/developer-api).

Icons: `anchor`, `briefcase`, `building`, `car`, `footprints`, `home`, `hospital`, `lock`, `map-pin`, `mountain`, `plane`, `shield`, `skull`, `star`, `tent`, `trees`, `waves`. Unknown names show a map pin.

## Spawns

`Config.Spawns` is the list everyone can see. A spawn is a table:

```lua title="Spawn definition"
{
    id = 'mrpd',
    label = 'Mission Row',
    description = 'Police Department',
    category = 'job',
    coords = vec4(428.23, -984.28, 29.76, 3.5),
    icon = 'shield',
    blip = 'radar_police_station',
    image = 'https://example.com/mrpd.webp',
    jobs = { police = 0, sheriff = 2 },
    onDuty = false,
    gangs = { ballas = 0 },
    ace = 'group.vip',
    lockedLabel = 'VIP only',
    hideLocked = false,
    vehicle = { model = 'police', plate = 'LSPD01', fuel = 100 },
    cinematic = true,
    order = 0,
    data = {},
}
```

| Field | Required | Description |
| --- | --- | --- |
| `id` | Yes | Unique string or number. `last` is reserved for the last location |
| `label` | Yes | Name in the list and on the map |
| `coords` | Yes | `vec4`, `vec3` or `{ x, y, z, w }` (also `h` / `heading`) |
| `description` | No | Second line |
| `category` | No | Category id, default `city` |
| `icon` | No | Overrides the category icon |
| `blip` | No | GTA blip image name, used instead of the icon |
| `image` | No | Picture for the card. Without one, the card shows the live preview or the map. Photos taken in the [editor](/new/spawn-selector/admin-tools) are used automatically |
| `jobs` | No | `'police'`, `{ 'police', 'sheriff' }` or `{ police = minGrade }` |
| `onDuty` | No | The job must be on duty |
| `gangs` | No | Same format as `jobs`, QBCore gangs |
| `ace` | No | ACE permission the player needs |
| `lockedLabel` | No | Text when locked. Default: "Police only" from the job label |
| `hideLocked` | No | Hide instead of showing it locked |
| `vehicle` | No | Model name, or `{ model, plate, props, fuel, keys }`: spawn in the driver seat |
| `cinematic` | No | Turns the camera flight on or off for this spot |
| `order` | No | Order inside its category |
| `data` | No | Anything. You get it back in `onSpawn` and the events |

## Editable files

**`client/editable.lua`**

| Function | Called when |
| --- | --- |
| `ClientEditable.Notify(desc, type)` | The selector shows a message |
| `ClientEditable.OnOpen(options)` | The map opens. Hide your HUD here |
| `ClientEditable.OnClose(spawn, options)` | The map closes (`spawn` is `nil` when cancelled). Show your HUD again |
| `ClientEditable.OnSpawned(spawn, options)` | The player landed |
| `ClientEditable.OnVehicleSpawned(vehicle, data)` | A spawn vehicle was created |
| `ClientEditable.CanOpen(options)` | Return `false` to block opening |

**`server/editable.lua`**

| Function | What it does |
| --- | --- |
| `ServerEditable.GetJob(source)` | Job used for locks: `{ name, label, grade, onDuty }` |
| `ServerEditable.GetGang(source)` | Gang used for locks (QBCore by default) |
| `ServerEditable.CanAccess(source, spawn, job)` | Return `false, 'Reason'` to lock a spot |
| `ServerEditable.GetSpawns(source, identifier)` | Return extra spawns for this player |
| `ServerEditable.IsAdmin(source)` | Who can use the admin commands and tabs |
| `ServerEditable.IsDead(source)` | Dead right now (QB metadata by default) |
| `ServerEditable.IsDeadOnLogin(source, identifier)` | Dead when the selector opens (QB metadata, ESX `users.is_dead`) |
| `ServerEditable.IsJailed(source, identifier)` | In jail (QB `injail`, ESX `users.jail_time` / `users.jail`) |
| `ServerEditable.OnCombatLog(identifier, record)` | A player left during combat |
| `ServerEditable.OnSpawned(source, spawn, isNew)` | The player landed |
