Koja Scripts

Admin menu

The /dispatchadmin menu: access, every page, the three modes, station placing with the gizmo, the scenario and fleet editors and the full list of settings.

Admin menu

/dispatchadmin opens the admin menu in game. Everything you change there is saved in the database (koja_dispatch_settings) and applies live, without a restart. The values in config.lua are the defaults.

Access

A player can open the menu when one of these is true:

  • their framework group is in Config.Admin.groups (admin, god, superadmin),
  • they have the ACE koja-dispatcher.admin (Config.Admin.ace).
server.cfg
add_ace group.admin koja-dispatcher.admin allow

The command is Config.Admin.command. It does nothing from the server console; use dispatchcheck there. Every admin change is printed in the server console and, with Discord logs on, posted to the admin webhook.

Pages

PageWhat it is for
OverviewLive state, current setup, statistics and quick actions
ModesCall sources, units, and what happens when nobody is on shift
StationsDispatch stations in the world
Access & jobsJobs allowed to dispatch, creating a job, hiring, dispatcher ranks
Calls & limitsDispatcher seats, remote CAD, generated call rate and limits
PayShift pay
FeaturesIntro camera, AI callers, sensors, map layers, field units
Integrationskoja-mdt and the client alert API
ModulesSettings of every optional module
ScenariosScenario editor
AI fleetFleet editor

Every setting has a reset button that shows the default value. Reset all settings puts every setting back to the config.lua value; stations, allowed jobs and created jobs are kept.

Overview

Refreshes every few seconds. It shows the dispatchers on shift (station and since when), units on duty per department with AI on and off duty and players, active incidents and open calls, the current setup (modes, stations, allowed jobs) and the incident statistics.

Quick actions affect everyone on the server at once:

ActionWhat it does
Test callRings a random generated 911 call in every CAD. Needs a dispatcher on shift.
Test incidentCreates a test incident about 120 m from you with one police unit and auto-assigns it.
Clear callsEnds every ringing and active call.
Clear incidentsCloses all incidents and frees their units.
End all shiftsClocks every dispatcher out. They see their shift report.

Modes

Three choices decide how the whole dispatch works. The page shows a live How calls flow diagram that changes with the modes.

Call sources (mode.calls)

ValueMeaning
generatedOnly AI 911 calls. The server makes up emergencies while dispatchers are on shift. Real calls (/911, phones, sensors, API, koja-mdt) are not rung at the console; they follow the "nobody on shift" rule below.
realOnly real calls: player /911, phones, sensors, other scripts through the API and koja-mdt. No generated calls.
mixedBoth. Real calls come in between generated ones.

Generated calls only come while at least one dispatcher is on shift.

Units (mode.units)

ValueMeaning
aiOnly AI units drive to incidents. Good for small or new servers.
playersOnly on-duty players with a police, EMS or fire job appear as units and get dispatched.
mixedPlayers and AI together. AI units of a department go off duty while units.disableWhenPlayers players of that department are on duty.

When nobody is on shift (mode.noDispatcher)

What happens to a real call (/911, phone, API, client alert) when no dispatcher can take it.

ValueMeaning
rejectThe caller is told nobody is answering. Nothing is created.
autoAn incident is created at the caller and the nearest free units are assigned automatically. The incident follows the caller until units arrive.
mdtThe call is sent to koja-mdt as a dispatch call for officers to pick up. Needs koja-mdt running with the integration on; otherwise it acts like reject.

Stations

Dispatch stations (consoles) in the world. A player whose job may dispatch walks up to one to start a shift. There can be up to 40 stations.

ActionWhat it does
Place a stationOpens the placing mode at your position.
RenameClick the name. 2 to 48 characters.
Stand / SitPose the dispatcher takes during the shift.
BlipShow the station on the map.
Adjust positionOpens the placing mode for this station with a live preview.
TeleportMoves you to the station.
Set waypointGPS to the station.
DeleteRemoves the station for good. A dispatcher on shift there is clocked out.

Placing a station with the gizmo

The admin menu hides, a camera orbits a see-through copy of your character that plays the exact sit or stand pose, and you place it with a 3D gizmo. The spot must stay within 25 m of where you stand.

InputAction
Drag the arrowsMove
Drag the ringRotate
TabSwitch between move and rotate mode
Q / ETurn 5° (Shift 1°, Ctrl 15°)
GSit / stand
RSnap to the floor
SSnap 5 cm / 5° on or off
FRefocus the camera
Right-drag / wheelOrbit / zoom the camera
X, Y, Z and heading fieldsExact values; arrow keys in a field nudge it (Shift x10)
EnterSave
EscCancel, nothing changes

Access & jobs

Jobs allowed to dispatch. A player needs one of these jobs at the minimum grade or higher. Only those players see the start prompt at a station. A job that the framework does not know is marked. With no job in the list nobody can start a shift. Removing a job does not end shifts that are running.

Create job (ESX and QBCore only). Name (lowercase, no spaces), label, grades (name, label, salary, up to 20) and the lowest grade that can dispatch. The job is created in the framework and added to the allowed list. On ESX it is created with ESX.CreateJob, on QBCore with exports['qb-core']:AddJob. On other frameworks add the job yourself.

Hire. Search online players by name, ID or job and set their job and grade. The list shows who is on shift and whether the grade can dispatch.

Dispatcher ranks. With the career module on, set the rank of online players. Setting a rank moves their XP to that rank's threshold.

Calls & limits

SettingDefaultWhat it does
Dispatchers at once (access.maxDispatchers)0Seats. 0 = no limit.
Remote CAD (access.remote)offAllowed jobs open the CAD anywhere with /cad.
Shortest / longest gap between calls (calls.intervalMin / calls.intervalMax)35 / 80 sTime between generated calls.
Faster with more dispatchers (calls.perDispatcherSpeedup)0.15Per extra dispatcher on shift.
Ringing calls at once (calls.maxRinging)4No new generated call while this many ring.
Active incidents limit (calls.maxActiveIncidents)10No new generated call while this many incidents are open.
Caller patience (calls.patience)45 sBefore a generated caller hangs up.
Player reply window (calls.playerReplyWindow)180 sHow long a player's 911 text line stays open.
Replace AI with players (units.disableWhenPlayers)3Only used when units are mixed. 0 = never.

The generated call settings are greyed out when call sources are real.

Pay

Paid when the shift ends: minutes worked plus a bonus per score point, scaled by the star rating, up to the maximum payout (0 = no cap). See Config.Pay.

Features

Dispatcher experience (intro camera, AI callers), automatic alerts (shots, crashes, downed players; only used when call sources include real calls; the switches apply live), map layers (civilians, NPC peds) and field units (scenes, auto waypoint).

Integrations

koja-mdt status (not installed, running, not started) and its five settings, the Client alerts switch for the client export SendAlert, and a copyable list of the developer exports. See Integrations.

Modules

Settings of the optional modules, one group per module: career, compat, discord, dynamics, fleet, hospitals, mdt_status, mdt_tools, patrol, phones, pursuit, stats, training, voice. They save and apply live like everything else. Settings that other resources add with the RegisterSetting export appear here too, grouped by the part of the key before the first dot. The full list is in All settings.

Scenarios

Create, edit, duplicate and disable the 911 scenarios generated calls are drawn from.

  • Built-in scenarios can be switched off and given another weight (how often they are drawn, 0 never). They cannot be deleted. Reset restores the default weight.
  • Custom scenarios (up to 100) are created from scratch or by duplicating a built-in one. Fields: name, category, priority, caller (any, male, female), distress, scene (none, crowd, fight, fire, injured, crash), 1 to 5 opening lines, answers per question with an optional chance and the extra units the answer reveals, base needs, correct instruction, respond, fail and work time, points, weight, needs transport, prank call, and where it happens (location tags, or up to 10 own places picked on the map or at your position; {location} inserts the place name).
  • Test call now rings the scenario at once.

Custom scenarios have no recorded voice lines, so their calls are text only unless AI callers are on. They are stored in koja_dispatch_scenarios.

AI fleet

Add, remove and disable AI units and stations. At first the fleet comes from Config.AiUnits. Press Copy config and edit to copy it into the editor; from then on the editor overrides Config.AiUnits.fleet and Config.AiUnits.stations.

  • Units: callsign, unit type and station (the type must match the station's department). Disabled units on an incident finish the job first; removed units are released at once. Up to 80 units.
  • Stations: name, department and position (click the map or use your position). A station with units cannot be removed. Up to 30 stations.
  • Reset to config deletes the edited fleet and respawns the units from Config.AiUnits.

If Config.AiUnits.enabled is false, no AI unit spawns even when listed here. The edited fleet is stored in koja_dispatch_ai_stations and koja_dispatch_ai_fleet.

All settings

Every key below can be changed in the menu, read with GetSetting and changed with SetSetting. Other resources can add their own keys with RegisterSetting (see Server exports). Defaults come from the config option named in the last column.

Core

KeyTypeRange or valuesDefaultWhat it does
mode.callsenumgenerated, real, mixedmixedWhere 911 calls come from. Config.Modes.calls
mode.unitsenumai, players, mixedmixedWhich units appear on the map and can be dispatched. Config.Modes.units
mode.noDispatcherenumreject, auto, mdtrejectWhat happens to a player's /911 when no dispatcher is working. Config.Modes.noDispatcher
access.maxDispatchersint0-320Dispatchers on shift at once, 0 no limit. Config.Dispatcher.maxDispatchers
access.remoteboolfalseAllowed jobs can open the CAD anywhere with /cad. Config.Dispatcher.remote
calls.intervalMinint10-60035Minimum seconds before the next generated call.
calls.intervalMaxint10-90080Maximum seconds before the next generated call.
calls.maxRingingint1-124No new generated call while this many are waiting.
calls.maxActiveIncidentsint1-5010No new generated call while this many incidents are open.
calls.patienceint15-30045Seconds a generated caller waits before hanging up.
calls.playerReplyWindowint30-900180Seconds a player's 911 chat stays open for replies.
calls.perDispatcherSpeedupnumber0-1, step 0.050.15Calls come this much faster for every extra dispatcher.
units.disableWhenPlayersint0-323AI of a department go off duty at this many players of it on duty, 0 never. Config.AiUnits.disableWhenPlayers
pay.enabledbooltruePay out when a shift ends.
pay.perMinuteint0-10006Money per minute worked.
pay.perPointint0-1002Bonus per score point.
pay.minimumMinutesint0-1203Shorter shifts get nothing.
pay.maximumint0-100000015000Cap per shift. 0 = no cap.
features.cinematicbooltrueCamera fly-in when a dispatcher clocks in.
features.aiCallersboolfalseLive AI callers via OpenAI / ElevenLabs. Needs the API keys. Config.AI.enabled
features.sensorDownedbooltrueA call when a player stays down.
features.sensorShotsbooltrueA call when someone shoots in the city. Applies live on every client.
features.sensorCrashbooltrueA call after a heavy vehicle crash. Applies live on every client.
features.showCiviliansbooltruePlayers without a service job appear as dots on the map.
features.showNpcPedsboolfalseNPC peds on the map. Costs performance on busy servers.
features.fieldScenesbooltrueUnits sent to an incident see props and peds at the scene.
features.fieldWaypointbooltrueSet the GPS when a unit gets assigned.
mdt.enabledbooltrueUse koja-mdt. Config.Mdt.enabled
mdt.pushIncidentsenumoff, real, allrealSend incidents to the MDT. Real = from players, API, manual and MDT; all = also generated calls.
mdt.pullCallsenumoff, calls, incidentscallsMDT calls (911, panic, ALPR...) ring in the CAD or open straight as an incident.
mdt.syncStatusbooltrueStatus and assignments sync both ways.
mdt.useCallsignsbooltruePlayer units show their MDT callsign.
api.clientAlertsboolfalseAllow the client export SendAlert. Rate limited. Config.Api.clientAlerts

Modules

KeyTypeRange or valuesDefaultWhat it does
career.enabledbooltrueXP, rank badge, simultaneous lines, call gates and supervisor powers.
career.xpMultipliernumber0.1-5, step 0.11.0Scales XP from score, incidents, codes and instructions.
career.xpTimeMultipliernumber0-5, step 0.11.0Scales XP per minute on shift.
career.xpPenaltyMultipliernumber0-5, step 0.11.0Scales XP lost for missed calls and failed incidents.
career.gateCallsbooltrueLow ranks cannot answer pursuit, hostage and mass casualty calls.
career.gateOwnershipbooltrueOnly supervisors can reassign units, cancel or reprioritise incidents another dispatcher owns.
compat.enabledbooltrueAccept alerts from other dispatch scripts.
compat.modeenumcall, incidentcallCall rings at the console, incident goes straight to the map.
compat.autoAssignbooltrueIncidents only: send the nearest free units at once.
discord.enabledbooltrueDiscord logging. Webhooks are read from server convars only.
discord.shiftsbooltrueLog shifts.
discord.incidentsbooltrueLog ended incidents.
discord.callsbooltrueLog missed and dropped calls.
discord.adminbooltrueLog admin changes.
discord.eventsbooltrueLog pursuits and MCI.
discord.minPriorityint1-33Lowest incident priority logged. 3 logs every incident, 1 only priority 1.
dynamics.eventsbooltrueScenarios with events can escalate while the caller is on the line or after dispatch.
dynamics.stagesbooltrueScenarios with stages continue with a new stage when the current one is done.
dynamics.weatherbooltrueGenerated calls depend on the in-game hour and weather.
dynamics.strengthnumber0-2, step 0.11.00 turns weighting off, 1 is normal, 2 doubles the effect.
fleet.enabledbooltrueAI units tire out and can be damaged on violent calls, then rest or repair at their station.
fleet.fatigueRatenumber0-30, step 0.56.0Fatigue (0-100) per minute on scene. Travel counts at about a third.
fleet.restSecondsint15-1800180Seconds a tired unit rests at its station.
fleet.repairSecondsint15-1800240Seconds a damaged unit spends in repair.
fleet.damageChancenumber0-1, step 0.050.25Damage chance on shooting, hostage, pursuit and assault calls (police full, EMS and fire less).
hospitals.enabledbooltrueTransports fill hospital beds. Full or diverted hospitals are skipped.
hospitals.turnoverSecondsint30-7200600Seconds until an occupied bed frees up.
mdt_status.enabledbooltrueField units set 10-codes with key mappings, /tencode and the quick menu.
mdt_status.hudbooltrueSmall HUD chip with the current code for field units.
mdt_status.backupUnitsint1-62Extra units auto-assigned when an officer requests backup.
mdt_status.cooldownint5-60045Seconds per officer between backup requests and panic fallbacks.
mdt_status.panicFallbackbooltrueWithout koja-mdt, 10-99 creates a priority 1 incident here.
mdt_tools.monitorbooltrueMDT workspace drawer in the CAD (key B).
mdt_tools.boloSourceenumauto, mdt, internalautoAuto uses koja-mdt when it runs and the internal list otherwise. MDT only refuses new BOLOs while it is down.
mdt_tools.alprbooltrueAlert dispatchers when an ALPR read hits a BOLO plate.
mdt_tools.notifyUnitsbooltrueSend internal BOLOs to on-duty police field units.
patrol.enabledbooltrueDispatchers can draw zones that idle AI units patrol. Turning it off recalls patrolling units.
patrol.maxZonesint1-328Zones at once.
patrol.speedFactornumber0.2-1, step 0.050.6AI speed while patrolling.
phones.enabledbooltrueCalls and texts to the service number reach the dispatcher like /911.
phones.resourceenumauto, lb-phone, qs-smartphone-pro, gksphone, npwd, yseriesautoauto uses the first supported phone that is running.
phones.numberint1-99999911Number players dial or text.
pursuit.enabledbooltruePursuit incidents get a moving suspect vehicle.
pursuit.maxActiveint0-102More pursuit incidents stay normal incidents.
pursuit.loseSecondsint10-30040Suspect lost when no unit or helicopter sees it for this long.
pursuit.spikesint0-103Spike strips per pursuit.
pursuit.roadblocksint0-102Roadblocks per pursuit.
pursuit.spikeChancenumber0-1, step 0.050.65Chance a spike strip slows the suspect.
pursuit.roadblockChancenumber0-1, step 0.050.5Chance the suspect stops at a roadblock.
pursuit.speedFactornumber0.5-2, step 0.051.15Suspect speed relative to a patrol car.
pursuit.escalationChancenumber0-1, step 0.050.3Chance a suspect flees in a vehicle when a robbery, assault or shooting escalates.
stats.enabledbooltrueStore every closed incident for the heatmap and the statistics.
stats.retentionDaysint1-36530Older incidents are deleted automatically.
training.enabledbooltrueDispatchers can run the guided tutorial from the top bar.
training.offerFirstShiftbooltrueOffer the tutorial to dispatchers with no finished shifts.
training.practiceCallsbooltrueEasy practice calls that only the trainee sees. No effect on score, pay or other dispatchers.
voice.enabledbooltruePrivate voice channel between dispatcher and player caller when a 911 call is answered.
voice.providerenumauto, pma-voice, mumble-voipautoAuto picks pma-voice first, then mumble-voip.
voice.channelBaseint1000-900000091100First call channel. Calls use the next 900 ids.
voice.endOnDispatchbooltrueOff: the caller stays on the line after dispatch until you end the voice call.
voice.allowTransferbooltrueDispatchers can hand the voice call to an on-duty player unit.

Module defaults come from the matching Config block (for example pursuit.maxActive from Config.Pursuit.maxActive). career.xp*, career.gate*, mdt_status.*, mdt_tools.* and training.* have their defaults in code.