
Emergency Response Simulator for FiveM
A guide to install Emergency Response Simulator for FiveM
- Crafted by Nights Software in collaboration with London Studios
📋 Table of Contents
- 🎥 Installation Tutorial
- 🛒 Purchase Information
- ⚠️ Important Pre-Installation Notes
- 🔧 System Requirements & Compatibility
- 📦 Installation Process
- ⚙️ Configuration Setup
- 🎯 Creating Custom Callouts
- 🔧 Open Source Functions / Events
- 🔗 night_shifts_mdt Integration — Field Ownership
- 🛠️ ERS Functions Reference
- 📤 Exports
- 🎨 Props by NovelaxNeko
- 🔧 Troubleshooting
- 💬 Support & Community
🎥 Installation Tutorial
Watch our comprehensive installation guide:
Installation Tutorial by Skull
🛒 Purchase Information
Get Emergency Response Simulator:
Purchase on Nights Software Store
⚠️ Important Pre-Installation Notes
Critical Installation Order: Always follow this exact sequence to avoid parsing errors in the F8 console:
- Set FTP Transfer Type to Binary
- Download ZIP Package from CFX Portal
- Unpack in a local folder
- Set File Transfer Protocol (FTP) to binary
- Drag files from local machine to server resources folder
- Add to server.cfg (ensure script)
- Boot up the server
Support Policy: Follow this guide step by step. If you’re stuck, ask for support in our Discord and provide the specific step name. Do not skip steps.
🔧 System Requirements & Compatibility
fxServer (Artifacts) & Gamebuild version
- ✅ Recommended Gamebuild: 3323
- ✅ Recommended Artifacts: 21703(+)
- ❌ Considered Not Compatible: Other Gamebuilds below 2944 and above 3323 and Artifacts.
Important: If you are using different Gamebuilds or Artifacts, trial and error this. Please inform us of (in)compatibility.
OneSync Requirements
- ✅ Required: OneSync Legacy or Infinity
- ⚠️ Note: Some hosting companies require setting
Enable Beyondto 1 in dashboard
Framework Compatibility
Standalone Mode
- ✅ Works without any framework
- ✅ Compatible with most known frameworks
ESX Integration
- Permissions: Configure job-based access in
night_ers/config/config.lua - Weapons: Configure in
night_ers/config/gear-config.lua - Code: Adjust item distribution in
night_ers/client/c_functions.lua
QBCore Integration
- Permissions: Configure job-based or QB permissions in
night_ers/config/config.lua - Weapons: Configure in
night_ers/config/gear-config.lua - Code: Adjust item distribution in
night_ers/client/c_functions.lua
Known Incompatibilities
Incompatible Resources:
- RemoveCops-AI
- Andrew’s Advanced AI
- Realistic Euphoria Physics
- RedSaints Stretcher/Ambulance
- Improved-Seat-Shuffle-FiveM by Dalrae1
- Clear world commands / Anti-cheat (resolve with your provider)
📦 Installation Process
Step 1: Download Required Resources
Download these packages from the CFX Portal in the exact order listed:
Core Resources (Required)
- SmartHoseLite (Required)
- Provided by London Studios
- Enables water hose functionality for fire extinguishing
- *Must start before
SmartFiresLite*—SmartFiresLiteconsumesSmartHoseexports at runtime.
- SmartFiresLite (Required)
- Provided by London Studios
- Enables fire spawning functionality
- Note: Keep Lite named
SmartFiresLite. If you use full Smart Fires instead, rename the folder to exactlySmartFires(case sensitive), per London Studios. This also goes for SmartFires v2:SmartFires.
- Emergency Response Simulator (Required)
- Main gamemode with 100+ callouts
- Supports Police, Fire, Medic, and Tow services
Optional Resources
- Night Discord API (Optional - Included with ERS Essential, Plus and Ultimate)
- Enables Discord role-based permissions
- View Features
- Installation Guide
- Night Subtitles (Optional - Included with ERS Essential, Plus and Ultimate)
- Movie-style subtitle display
- View Features
- Drag-and-drop installation
- Nearest Postal & Map (Optional - Free)
- Default postal codes with custom map
- CFX Forum Post
- Direct Download
- Map Download
- Night Shifts MDT (Optional - Included with ERS Plus and Ultimate)
- Modern MDT for emergency services management
- View Features
- Installation Guide
- Theebu Flatbeds Lite (Optional - Included with ERS Essential, Plus and Ultimate)
- Vehicle transport functionality
- Full Version available
Add-on DLCs
- World Events Add-on (Optional)
- Dynamic Weighing Stations (Optional)
- K9 Dog Handler (Optional)
Step 2: File Placement
Critical: Place all resources in your resources folder and DO NOT RENAME them. Use exact names as specified in this documentation.
Step 3: Server Configuration
Add to your server.cfg in exact order:
# Optional Resources
ensure night_discordapi
ensure night_subtitles
ensure nearest-postal
ensure map
# Required Resources
ensure SmartHoseLite # or SmartHose if you have full version (must start before SmartFiresLite)
ensure SmartFiresLite # or SmartFires if you have full version
# Core ERS
ensure night_shifts_mdt
ensure night_ers
# Optional Add-ons
ensure ebu_flatbeds_ers
ensure night_ers_worldevents
ensure night_ers_weighstation
ensure night_ers_k9
# Optional Callout Packs
ensure night_ers_cp1
⚙️ Configuration Setup
Required Tools
Visual Studio Code: We strongly recommend downloading VS Code for editing Lua files.
Configuration Files
| File | Purpose |
|---|---|
night_ers/config/config.lua | Main configuration |
night_ers/config/gear-config.lua | Weapons and clothing |
night_ers/config/impound-config.lua | Impound settings |
night_ers/config/leaderboard-config.lua | Leaderboard settings |
night_ers/config/npcbackup-config.lua | NPC Backup settings |
night_ers/config/persistententity-config.lua | NPC personal data settings |
night_ers/config/pullover-config.lua | Traffic stop settings |
night_ers/config/pursuit-config.lua | Pursuit settings |
night_ers/config/questioning-config.lua | NPC questioning settings |
night_ers/config/sound-config.lua | Sound & Voice over settings |
night_ers/config/spikestrip-config.lua | Spikestrip settings |
night_ers/config/stretcher-config.lua | Stretcher settings |
night_ers/config/vehinteractions-config.lua | Vehicle search settings |
night_ers/client/c_functions.lua | Client-side functions / events |
night_ers/server/s_functions.lua | Server-side functions / events |
Configuration Process
- Open VS Code and navigate to the config files Download Visual Studio Code
- Read thoroughly - each line has explanatory comments
- Configure in order - work from top to bottom
- Watch for notes - important warnings are clearly marked
- Test frequently - use F8 console and server console for error checking
Time Investment: Plan adequate time for configuration. Each variable is named descriptively to help you understand its purpose.
🎯 Creating Custom Callouts
Overview
ERS includes open-source callout scripts in night_ers/callouts/plugins/*.lua. You can add, remove, or edit callouts using built-in ERS functions.
Callout Creator Pack
Free Resource: Download our Callout Creator Pack for examples and documentation.
Development Process
- Copy Template: Use existing callout file from
night_ers/callouts/plugins/ - Rename File: Use your desired callout name
- Modify Code: Adjust config, client, and server sections
- Test Thoroughly: Restart script and test with
/requestcallout
Support Notice: Nights Software does not provide custom callout creation support beyond this documentation. Ask the community for help!
🔧 Open Source Functions / Events
Client Functions / Events (night_ers/client/c_functions.lua)
Event Triggers
--- Handles when a callout is offered to the player.
-- @param calloutData table The data of the callout.
function OnIsOfferedCallout(calloutData)
-- Add your code here. Keep in mind they are offered a callout. It is possible they will not accept the callout.
-- local jsonReady = CloneWithoutFunctions(calloutData)
TriggerServerEvent('ErsIntegration::OnIsOfferedCallout', calloutData)
end
--- Handles when a callout is accepted by the player.
-- @param calloutData table The data of the callout.
function OnAcceptedCalloutOffer(calloutData)
-- Add your code here. Keep in mind they have accepted a callout. It is possible they will cancel before arrival (and spawn of entities).
TriggerServerEvent('ErsIntegration::OnAcceptedCalloutOffer', calloutData)
end
--- Handles when the player arrives at a callout.
-- @param calloutData table The data of the callout.
function OnArrivedAtCallout(calloutData)
-- Add your code here. This is triggered right before the entities are built for a callout. This code will execute first.
TriggerServerEvent('ErsIntegration::OnArrivedAtCallout', calloutData)
end
--- Handles when a callout is ended (as the host). This does not mean the callout is completed.
-- @param calloutData table The data of the callout.
function OnEndedACallout(calloutData)
-- Add your code here. This is triggered right before the entities are deleted or callout is cancelled serverside. This code will execute first.
TriggerServerEvent('ErsIntegration::OnEndedACallout', calloutData)
end
--- Handles when a callout is completed successfully.
-- @param calloutData table The data of the callout.
function OnCalloutCompletedSuccesfully(calloutData)
-- Add your code here. This is triggered right after the entire callout task list is completed.
TriggerServerEvent('ErsIntegration::OnCalloutCompletedSuccesfully', calloutData)
end
--- Handles when a pullover is initiated.
-- @param pedData table The data of the ped.
-- @param vehicleData table The data of the vehicle.
function OnPullover(pedData, vehicleData)
-- Add your custom pullover logic here or trigger (and build) a server event to handle it.
TriggerServerEvent('ErsIntegration::OnPullover', pedData, vehicleData)
end
--- Handles when a pullover is ended.
-- @param pedData table The data of the ped.
-- @param vehicleData table The data of the vehicle.
function OnPulloverEnded(pedData, vehicleData)
-- Add your custom pullover ended logic here or trigger (and build) a server event to handle it.
TriggerServerEvent('ErsIntegration::OnPulloverEnded', pedData, vehicleData)
end
--- Handles when a pursuit is started.
-- @param pedData table The data of the ped.
function OnPursuitStarted(pedData)
-- Add your custom pursuit started logic here or trigger (and build) a server event to handle it.
TriggerServerEvent('ErsIntegration::OnPursuitStarted', pedData)
end
--- Handles when a pursuit is ended.
-- @param pedData table The data of the ped.
function OnPursuitEnded(pedData)
-- Add your custom pursuit ended logic here or trigger (and build) a server event to handle it.
TriggerServerEvent('ErsIntegration::OnPursuitEnded', pedData)
end
- Explore the file for more…
Server Functions / Events (night_ers/server/s_functions.lua)
--- Handles user shift toggle events.
-- @param src number The source ID of the user who toggled shift.
-- @param isOnShift boolean Whether the user is now on shift or off shift.
-- @param serviceType string The service type of the shift (police, fire, ambulance, tow).
RegisterServerEvent("ErsIntegration::OnToggleShift")
AddEventHandler("ErsIntegration::OnToggleShift", function(source, isOnShift, serviceType)
-- Add your custom shift toggle logic here
-- print(source, isOnShift, serviceType)
end)
--- Handles first-time NPC interaction events.
-- @param src number The source ID of the user who interacted with the NPC.
-- @param pedData table The complete data table of the NPC being interacted with.
-- @param context string The interaction context:
--
-- Timing & data source:
-- * Without night_shifts_mdt, this event fires immediately with ERS-generated
-- random identity data — legacy behaviour.
-- * With night_shifts_mdt running, this event fires ONCE per first interaction,
-- AFTER the MDT identity lookup has merged into pedData. Identity fields come
-- from the MDT's persistent civilian record — the same values the officer sees
-- in the MDT UI.
-- * License_* and FlagsOrMarkers will be nil when the MDT is active. Query the
-- MDT exports for warrants/flags/licenses instead.
-- * If the MDT identity never resolves within ~5s, the event still fires
-- once with the partial pedData and identity fields stay nil.
RegisterServerEvent("ErsIntegration::OnFirstNPCInteraction")
AddEventHandler("ErsIntegration::OnFirstNPCInteraction", function(source, pedData, context)
-- Add your custom NPC interaction logic here
--[[
Context frequency analysis:
- "on_interaction": Very common (normal ped interactions)
- "on_aiming_at_ped": Common (when aiming at peds and ordering them to kneel)
- "on_pullover": Common (traffic stops)
- "on_pursuit_start": Uncommon (callout/world event peds fleeing, regular peds are usually interacted with first)
- "on_pursuit_end": Very rare (edge cases only)
- "on_pullover_end": Very rare (edge cases only)
]]
-- print(source, json.encode(pedData, { indent = true }), context)
end)
-- Handles first vehicle interaction events.
-- @param src number The source ID of the user who interacted with the vehicle.
-- @param vehicleData table The complete data table of the vehicle being interacted with.
-- @param context string The interaction context:
--
-- Timing & data source:
-- * Without night_shifts_mdt, this event fires immediately with ERS-rolled
-- random compliance data — legacy behaviour.
-- * With night_shifts_mdt running, this event fires ONCE per first interaction,
-- AFTER the MDT vehicle lookup has merged into vehicleData. Compliance fields
-- and owner_name come from the MDT's persistent vehicle record. For stolen
-- vehicles, owner_name is the victim's name (taken from identity.owner)
-- rather than the driver.
-- * If the MDT identity never resolves within ~5s, the event still fires
-- once with the partial vehicleData (compliance fields stay nil).
RegisterServerEvent("ErsIntegration::OnFirstVehicleInteraction")
AddEventHandler("ErsIntegration::OnFirstVehicleInteraction", function(source, vehicleData, context)
-- Add your custom vehicle interaction logic here
--[[
Context frequency analysis:
-- Explained: "First interaction with this specific vehicle instance in current session, unless the network ID was replaced. Then it can be an other instance for the same vehicle."
- "on_pullover": Very common (traffic stops)
- "on_vehicle_search": Common (when searching for vehicles)
- "on_pursuit_start": Uncommon (callout/world event peds fleeing whilst in a vehicle and not interacted with yet)
- "on_pullover_end": Very rare (edge cases only)
]]
-- print(source, json.encode(vehicleData, { indent = true }), context)
end)
--- Handles when a callout is offered.
-- @param calloutData table The data of the callout.
RegisterServerEvent("ErsIntegration::OnIsOfferedCallout")
AddEventHandler("ErsIntegration::OnIsOfferedCallout", function(calloutData)
local src = source
-- Add your custom callout offered logic here
-- print(src, calloutData)
end)
--- Handles when a callout is accepted.
-- @param calloutData table The data of the callout.
RegisterServerEvent("ErsIntegration::OnAcceptedCalloutOffer")
AddEventHandler("ErsIntegration::OnAcceptedCalloutOffer", function(calloutData)
local src = source
-- Add your custom callout accepted logic here
-- print(src, calloutData)
end)
--- Handles when a callout is arrived at.
-- @param calloutData table The data of the callout.
RegisterServerEvent("ErsIntegration::OnArrivedAtCallout")
AddEventHandler("ErsIntegration::OnArrivedAtCallout", function(calloutData)
local src = source
-- Add your custom callout arrived at logic here
-- print(src, calloutData)
end)
--- Handles when a callout is ended.
RegisterServerEvent("ErsIntegration::OnEndedACallout")
AddEventHandler("ErsIntegration::OnEndedACallout", function()
local src = source
-- Add your custom callout ended logic here
-- print(src)
end)
--- Handles when a callout is completed successfully.
-- @param calloutData table The data of the callout.
RegisterServerEvent("ErsIntegration::OnCalloutCompletedSuccesfully")
AddEventHandler("ErsIntegration::OnCalloutCompletedSuccesfully", function(calloutData)
local src = source
-- Add your custom callout completed successfully logic here
-- print(src, calloutData)
end)
--- Handles when a pullover is initiated.
-- @param pedData table The data of the ped.
-- @param vehicleData table The data of the vehicle.
RegisterServerEvent("ErsIntegration::OnPullover")
AddEventHandler("ErsIntegration::OnPullover", function(pedData, vehicleData)
local src = source
-- Add your custom pullover logic here
-- print(src, pedData, vehicleData)
end)
--- Handles when a pullover is ended.
-- @param pedData table The data of the ped.
-- @param vehicleData table The data of the vehicle.
RegisterServerEvent("ErsIntegration::OnPulloverEnded")
AddEventHandler("ErsIntegration::OnPulloverEnded", function(pedData, vehicleData)
local src = source
-- Add your custom pullover ended logic here
-- print(src, pedData, vehicleData)
end)
--- Handles when a pursuit is started.
-- @param pedNetId number The network ID of the ped.
RegisterServerEvent("ErsIntegration::OnPursuitStarted")
AddEventHandler("ErsIntegration::OnPursuitStarted", function(pedData)
local src = source
-- Add your custom pursuit started logic here
-- print(src, pedData)
end)
--- Handles when a pursuit is ended.
-- @param pedData table The data of the ped.
RegisterServerEvent("ErsIntegration::OnPursuitEnded")
AddEventHandler("ErsIntegration::OnPursuitEnded", function(pedData)
local src = source
-- Add your custom pursuit ended logic here, note that it's possible that the ped is being deleted on losing the target.
-- print(src, pedData)
end)
- Explore the file for more…
🔗 night_shifts_mdt Integration — Field Ownership
When the optional night_shifts_mdt resource is running, the MDT becomes the single source of truth for identity and vehicle compliance data. ERS resolves the MDT identity on the client, BEFORE asking the server for pedData / vehicleData, then forwards the resolved identity inline so the server can merge it synchronously. The OnFirstNPCInteraction / OnFirstVehicleInteraction events fire once, in the same tick as the underlying request, with MDT-authoritative values already overlaid into the existing ERS field shape — external integrations that subscribe to these events keep working without any code changes and never see an unmerged placeholder payload.
NPC payload (pedData)
| Field | With night_shifts_mdt | Without night_shifts_mdt |
|---|---|---|
FirstName, LastName, DOB, Gender | MDT (persistent civilian record) | ERS (random rolls) |
Address, City, PostalCode, State, Country | MDT | ERS |
Email, PhoneNumber, Nationality | MDT | ERS |
ProfilePicture | MDT (pictureUrl) | ERS (model-derived) |
License_Car, License_Bike, License_Boat, License_Truck, License_Pilot (+ _Is_Valid / _Colour / _Icon per line, e.g. License_Car_Is_Valid) | Same shape as plain ERS — same pedData keys. With MDT, each line is filled from the civilian’s PNC licence row for that fixed type id: License_Car → driver_license, License_Bike → motorcycle_license, License_Boat → boat_license, License_Truck → commercial_driver_license, License_Pilot → pilot_license. Those ids are not configurable (only the labels in admin). If there is no row for a type, the card still shows the slot, usually as no licence. Without MDT, ERS random rolls. | ERS (random rolls) |
FlagsOrMarkers | Use it like vanilla ERS. Keep reading pedData.FlagsOrMarkers on your existing server events (OnFirstNPCInteraction, pullover, etc.). With the MDT on, that object reflects the civilian’s Important Notices from the MDT instead of a random roll. You do not need a different field or a second integration path. | ERS (random rolls) |
mdtCivilianId, mdtPersonalId | Set on **mdt-merged** payloads — MDT civilian PK and dossier id for integrations | (n/a — MDT not running) |
AddressType | ERS (always — situational) | ERS |
Inventory, isDrunk, isDrugged, BehaviourState | ERS (always — situational) | ERS |
MassiveBleeding, Airway, Breathing, Circulation, Hypothermia, CPR | ERS (always — medical) | ERS |
Vehicle payload (vehicleData)
| Field | With night_shifts_mdt | Without night_shifts_mdt |
|---|---|---|
tax, mot, insurance, stolen, bolo, bolo_description | MDT (persistent vehicle record) | ERS (random rolls) |
owner_name | MDT (identity.owner.firstName + lastName — falls back to driver name) | ERS (driver / random) |
build_year | MDT if known (vehicle.buildYear), else ERS roll | ERS (random roll) |
make, model, color, vehicle_class, license_plate, vehicle_picture_url | ERS (vehicle attributes) | ERS |
inventory | ERS (always — situational) | ERS |
Debug printing
When Config.Debug = true, every OnFirst* event payload is pretty-printed to the server console with a mode tag indicating how the data was sourced:
| Mode | Meaning |
|---|---|
mdt-merged | MDT identity resolved on the client and merged into the payload before firing. The common path when night_shifts_mdt is running. |
mdt-disabled | night_shifts_mdt is not running. ERS data fired immediately (legacy behaviour). |
mdt-no-civilian | Ped data was requested without an MDT-merged civilian id (non-standard integration path). |
mdt-no-vehicle | Vehicle data was requested without MDT-backed vehicle identity (non-standard integration path). |
Use these tags when debugging server-side integrations to confirm which sourcing path fired for each OnFirst* event.
🛠️ ERS Functions Reference
Client Functions
| Function | Description |
|---|---|
ERS_SetMovementAnimClipSetToPed(ped, clipset) | Apply movement style |
ERS_SpawnConfiguredWeaponForPed(ped, calloutDataClient) | Spawn weapon by chance |
ERS_RequestNetControlForEntity(entityId) | Request entity control |
ERS_PerformTimedActionOnPed(calloutDataClient, pedList) | Execute pre-configured ped behaviors |
ERS_CreateTemporaryBlipForEntities(entityList, timeoutInMs) | Create temporary blips |
ERS_SpawnParticlesWithinRange(coords, diameter, particleDict, particleName, particleSize, particleAmount, timeout, chanceToExplode) | Spawn particles with explosion chance |
ERS_GetRandomCoordinateWithinRangeOfCoordinate(coords, diameter) | Get random coordinates |
ERS_SetPedToFleeFromPlayer(ped) | Make ped flee |
ERS_SetPedToAttackPlayer(ped) | Make ped attack |
ERS_SetPedToSurrender(ped) | Make ped surrender |
ERS_ApplyBloodToPed(ped) | Apply blood to ped |
ERS_CreateBloodPuddleAtPed(ped) | Create blood puddle |
ERS_SetPedToPassout(ped) | Make ped pass out |
ERS_ClearPedTasksAndBlockEvents(ped) | Clear ped tasks |
ERS_SetPedAsDrunkPed(ped) | Apply drunk movement |
ERS_GetIsPedADrunkPed(ped) | Check if ped is drunk |
ERS_CheckIfPedIsAlive(ped) | Check ped health status |
ERS_IsPedAnAnimalPed(ped) | Check if ped is animal |
ERS_SetRandomDamageToVehicle(veh) | Apply vehicle damage |
ERS_CreateFlareAtCoordinate(coords) | Create red flare |
ERS_GetSafeSpawnPointForNPCVehicle() | Find safe spawn point |
ERS_SelectRandomMovementClipSet() | Get random movement style |
ERS_SelectMentalHealthPersonScenario() | Get mental health scenario |
ERS_SelectRandomBystanderScenario() | Get bystander scenario |
ERS_SelectRandomProtesterScenario() | Get protester scenario |
ERS_SelectRandomWoundedPersonScenario() | Get wounded person scenario |
ERS_SelectStandingByFireScenario() | Get fire scenario |
ERS_PedEquipWeapon(ped, weaponModel) | Equip weapon to ped |
ERS_DeleteEntityFromCallout(entityId) | Delete entity |
Server Functions
| Function | Description |
|---|---|
ERS_CreatePed(model, coords, heading) | Create synchronized ped |
ERS_CreateObject(model, coords, heading) | Create synchronized object |
ERS_CreateVehicle(model, vehType, coords, vehHeading) | Create synchronized vehicle |
ERS_GetRandomCoordinateWithinRangeOfCoordinate(coords, diameter) | Get random coordinates |
ERS_GetRandomModel(modelList) | Select random model |
📤 Exports
Client Exports (night_ers/client/exports_client.lua)
-- Get player status
local isOnShift = exports['night_ers']:getIsPlayerOnShift()
local getPlayerActiveServiceType = exports['night_ers']:getPlayerActiveServiceType()
local isPlayerAttachedToCallout = exports['night_ers']:getIsPlayerAttachedToCallout()
local isPlayerTrackingUnit = exports['night_ers']:getIsPlayerTrackingUnit()
-- Other
exports['night_ers']:playRadioAnimation()
exports['night_ers']:toggleDispatchMessages()
exports['night_ers']:toggleHints()
exports['night_ers']:toggleShift()
exports['night_ers']:trackPlayerCallout(targetSource)
exports['night_ers']:ERS_PedEquipWeapon(pedEntityId, weaponModelName, ammo)
exports['night_ers']:SetERSVehicleInfoDisplay(display) -- Sets the display for vehicle information on traffic stops to true or false.
exports['night_ers']:SetERSIDCardInfoDisplay(display) -- Sets the display for ID cards to true or false.
Start pursuit (callouts & other integrations)
Use from client Lua in resources that ensure / dependency on night_ers. There is no server export for this; if your logic runs on the server, trigger the officer’s client (e.g. your own event) and call the export there.
| Export | Notes |
|---|---|
StartPursuit(pedNetId) | pedNetId is the suspect’s network id (e.g. NetworkGetNetworkIdFromEntity(suspectPed)). Returns **true** if ERS entered pursuit mode, **false** if it refused (pursuit disabled in config, player not on police service, invalid net id, already in a pursuit or cancel-in-progress, or ped / vehicle data could not be resolved for pursuit init). |
After a successful start, the player is in pursuit mode and you can use **RequestNPCPursuitBackup** / **CancelNPCPursuitBackup** as documented below.
local netId = NetworkGetNetworkIdFromEntity(suspectPed)
local started = exports['night_ers']:StartPursuit(netId)
NPC backup — pursuit & service (other resources)
Use these from client scripts in resources that ensure / dependency on night_ers. Pursuit backup exports only work while the player is in pursuit mode (including after a successful **StartPursuit** from another resource).
| Export | Notes |
|---|---|
RequestNPCPursuitBackup(unitType) | Request pursuit NPC backup, or toggle-cancel by calling again with the same unitType while pending. Returns dispatched, reason. |
CancelNPCPursuitBackup(serverDeleteDelayMs?) | Cancels active pursuit NPC backup without a unitType. Optional delay (ms) for server-side cleanup; omit or 0 for immediate. |
RequestNPCBackup(backupType) | Service / road NPC backup. Returns dispatched, reason; when dispatched is false, reason is "unknown_backup_type". |
CancelNPCBackup(backupType, mustNotify) | Cancels matching service backup. mustNotify controls in-game notification behaviour (bool). |
Pursuit unitType must match night_ers/config/pursuit-config.lua → Config.PursuitBackupTypes[].PursuitType (stock values, all lowercase):
"light", "medium", "heavy", "air", "army"
Service backupType identifiers:
"ambulance", "police", "taxi", "tow", "roadservice", "coroner", "animalrescue", "mechanic", "fire"
Service backup casing: You can pass any casing (e.g.
"Tow"or"TOW").night_ersnormalizesbackupTypeinternally for both request and cancel. PursuitunitTypestill must match config exactly unless you customizePursuitTypestrings.
Toggle cancel (pursuit): Calling
RequestNPCPursuitBackupagain with the sameunitTypewhile a pursuit backup is already pending cancels that request (returnsdispatched,reason). UseCancelNPCPursuitBackupwhen you want to cancel without knowing the pending type.
-- Pursuit NPC backup — only during pursuit mode; returns dispatched, reason
local dispatched, reason = exports['night_ers']:RequestNPCPursuitBackup('light')
-- Toggle-cancel: call again with same unitType while pending
-- local dispatched2, reason2 = exports['night_ers']:RequestNPCPursuitBackup('light')
-- Explicit pursuit cancel (optional server purge delay in ms)
exports['night_ers']:CancelNPCPursuitBackup()
-- exports['night_ers']:CancelNPCPursuitBackup(5000)
-- Service NPC backup — returns dispatched, reason (second value set when not dispatched, e.g. "unknown_backup_type")
local dispatched, reason = exports['night_ers']:RequestNPCBackup('tow')
exports['night_ers']:CancelNPCBackup('tow', true)
Server Exports (night_ers/server/exports_server.lua)
-- Shift management
exports['night_ers']:toggleShift(source, shiftType) -- "police", "ambulance", "fire", "tow"
exports['night_ers']:trackPlayerCallout(source, targetSource)
exports['night_ers']:setPlayerCalloutOffersEnabled(source, enabled)
-- Advanced programming exports:
exports['night_ers']:getCallouts() -- Returns a json ready table of all callouts in the callouts/plugins/*.lua folder. (Without functions!)
exports['night_ers']:createCallout(callout) -- Only use if you know what you are doing, this allows you to adjust some variables to when spawning callouts via an external program.
-- Custom callouts / pack server Lua — see **Spawning Smart Fires (server)** for the safe exports.
-- Scripted callout *offer* (recommended for integrations — returns whether the prompt was shown):
local ok, reason = exports['night_ers']:SendCalloutOfferToPlayer(source, optionalCalloutId, optionalTimeoutMs)
createCallout vs SendCalloutOfferToPlayer (server scripts)
createCalloutmakes a new callout preset in memory by copying an existing definition. It merges things likeCalloutLocations,PedWeaponData, and AI / loot rolls. You only get{ calloutId = "<new-key>" }. It never checks the player — not shift, job, distance, “already busy”, or whether callouts are on. Nobody gets a popup; you spawn or drive the scenario from your own script.
Use SendCalloutOfferToPlayer when you want the same thing as /requestcallout: eligibility runs on server + client (shift, cooldown, blocks, passenger, valid spots, …) and the player sees the normal accept / decline dispatch prompt.
| Argument | Meaning |
|---|---|
source | Player’s server id. |
optionalCalloutId | Leave off or use nil / "" for a random callout (same idea as typing /requestcallout with no id). Or pass a callout id string (same loose matching as /requestcallout). |
optionalTimeoutMs | How many milliseconds to wait for that player’s client to answer. Default is 5000 if you skip this argument. |
Waiting on the player
This export talks to their client. Your server script pauses until the client answers or the wait time runs out.
- Omit the third argument → wait 5000 ms (5 seconds) at most.
- Pass a positive number → wait that many milliseconds (e.g.
15000= 15 seconds). - If time runs out with no reply → you get
ok = falseandreason = "client_response_timeout"(crash, disconnect, extreme lag).
Returns
ok—trueif the offer went through.reason— only whenokisfalse; log it to see what blocked the offer.
Spawn spots (matches /requestcallout)
- Random offer (
nil/"") → spawn must be within range of the player (seeOfferCalloutsWithinRangeOfin config). - You pass a specific id → any spawn listed for that callout can be used (range filter off).
When ok is false — common reason values
Server: invalid_player, player_not_on_shift, no_active_service, player_busy_on_callout, no_callouts_enabled_on_server, callout_service_mismatch, callout_not_found:..., no_eligible_callouts_for_service, offer_unavailable, client_response_timeout
Client / player-side: callouts_disabled_by_player, callouts_temporarily_blocked, callout_request_cooldown, not_on_shift, passenger_not_allowed, already_offered_or_attached, no_valid_locations
Example:
local ok, err = exports['night_ers']:SendCalloutOfferToPlayer(src)
if not ok then
print(('Could not offer callout: %s'):format(err or '?'))
else
-- Player has the accepting / declining timed prompt exactly like `/requestcallout`.
end
local ok2, err2 = exports['night_ers']:SendCalloutOfferToPlayer(src, 'callout_vehicle_theft', 6000)
Spawning Smart Fires (server)
This is mainly for custom callouts: if you write your own Lua (in night_ers callout plugins, a separate callout pack resource, or any other server script that participates in building a callout) and you need to start a Smart Fires fire or smoke.
If you use Smart Fires or Smart Fires Lite with ERS, stock callouts already handle setup and cleanup—you do not need these exports unless your script creates the flame or smoke.
From that server Lua, call night_ers rather than Smart Fires directly. Fires you create this way behave like stock ERS callouts on your server—the same cleanup when the incident ends and the same integration with whichever Smart Fires product you run with ERS.
GetFireConfig(): In custom callouts, call exports['night_ers']:GetFireConfig() on the server when you need size/type values. It exposes the same tables core fire callouts draw from so your random sizes and fire types stay consistent. The example below uses the returned booleans only to distinguish full Smart Fires from Lite sizing (you can paste that pattern verbatim).
| Export | What you pass in | What you get back |
|---|---|---|
ERS_CreateSmartFireAtCoords | World position, size (radius number), fire type (string) | Fire / incident id to store on your callout, or nothing if Smart Fires is turned off in ERS or the fire resource is not running. |
ERS_CreateSmartSmokeAtCoords | World position, size, smoke type (string) | Smoke id (or equivalent), or nothing if unavailable. |
ERS_StopSmartFire | Id you received when creating the fire | Removes that fire the same way ERS would when cleaning up. |
ERS_StopSmartSmoke | Id you received when creating the smoke | Removes that smoke the same way ERS would. |
-- Example pattern for custom callouts (your coordinates and lists).
local fireCfg = exports['night_ers']:GetFireConfig()
if fireCfg.UsingSmartFiresV2 or fireCfg.UsingSmartFires then
local fireSize = fireCfg.RandomMediumFireOrSmokeSize[math.random(#fireCfg.RandomMediumFireOrSmokeSize)]
local fireType = fireCfg.NormalFireTypes[math.random(#fireCfg.NormalFireTypes)]
local fireId = exports['night_ers']:ERS_CreateSmartFireAtCoords(vector3(x, y, z), fireSize, fireType)
if fireId ~= nil then table.insert(fireList, fireId) end
else
local fireSize = fireCfg.RandomSmallFireOrSmokeSize[math.random(#fireCfg.RandomSmallFireOrSmokeSize)]
local fireId = exports['night_ers']:ERS_CreateSmartFireAtCoords(vector3(x, y, z), fireSize, "normal")
if fireId ~= nil then table.insert(fireList, fireId) end
end
Auto cleanup: The example
table.insert(fireList, fireId)is how you register “this incident owns that fire”. When your callout is torn down normally, ERS walksfireList/smokeListand stops anything still listed.Manual stop: Use
ERS_StopSmartFire/ERS_StopSmartSmokeonly when you extinguish or remove smoke in the middle of the callout (your script clears it early). Otherwise you don’t need those calls—the end-of-callout cleanup handles it if the id stays infireList/smokeList.
🎨 Props by NovelaxNeko
10 Free Included Props
| Name | Prop Model |
|---|---|
| Cone | neko_night_cone_00 |
| Barrier | neko_night_water_barrier_00 |
| Warning triangle | neko_night_warning_tri_00 |
| Rubber barrier | neko_night_rubber_barrier_00 |
| Barrier | neko_night_barrier_00 |
| Barrier 1 | neko_night_barrier_01 |
| Barrier 2 | neko_night_barrier_02 |
| Arrow board cross | neko_night_arrow_board_00 |
| Arrow board left | neko_night_arrow_board_00_l |
| Arrow board right | neko_night_arrow_board_00_r |
Artist Credit: Check out NovelaxNeko!
🔧 Troubleshooting
Common Issues
Fire & Smoke Problems
Resource Naming: SmartFires and SmartHose must be named exactly
SmartFiresandSmartHose(case sensitive). Same applies to Lite versions. A common issue is that callouts can no longer be cancelled or will no longer spawn after encountering issues due to faulty naming of the resources.
MDT Integration Issues
Solution: Enable ERS in
night_shifts_mdtconfig and enable Night Shifts innight_ersconfig. Duty can (optionally) toggled through the MDT
Hosting-Specific Issues
- Iceline Hosting: Set
Enable Beyondto 1 in server dashboard - Entity Spawning: Ensure OneSync is properly configured in the host dashboard
Compatibility Checklist
- ✅ Recommended Gamebuild: 3323
- ✅ Recommended Artifacts: 21703(+)
- ❌ QBox important note; NPC Backup doesn’t spawn: Remove blocked ped & vehicle model names, like
s_m_y_cop_01orFIRETRUKfromqbx_smallresources/qbx_entitiesblacklist/config.luaand your backup peds will spawn again. - ❌ Considered Not Compatible: Other Gamebuilds below 2944 and above 3323 and Artifacts.
- ❌ RemoveCops-AI: We recommend to disable this resource when using ERS.
- ❌ Andrew’s Advanced AI: We recommend to disable this resource when using ERS.
- ❌ Realistic Euphoria Physics: We recommend to disable this resource when using ERS.
- ❌ RedSaints Stretcher/Ambulance: We recommend to disable this resource when using ERS.
- ❌ Improved-Seat-Shuffle-FiveM by Dalrae1: We recommend to disable this resource when using ERS.
- ⚠️ Anti-cheat: May cause entity deletion issues. This can be prevented via your anti-cheat settings. Contact your provider.
💬 Support & Community
Getting Help
- Read Documentation: Review this guide thoroughly
- Check Console: Use F8 and txAdmin (sserver) console for error messages
- Discord Support: Create a ticket in our Discord
Feedback & Reviews
We welcome your feedback! Visit our Discord for:
- Support
- Product reviews
- Feature suggestions
- Documentation improvements