Documentation

FiveM Script Docs

General server guides and documentation for all SWR Entertainment FiveM scripts.

Installing a FiveM Resource

General Guide

Follow these steps to install any FiveM resource, including SWR scripts, on your server.

Steps

  1. 1Download the resource .zip from your Tebex receipt, the release page, or wherever the script was provided.
  2. 2Extract the archive. Confirm the extracted folder contains a fxmanifest.lua or __resource.lua at its root. That is your resource folder.
  3. 3Move the resource folder into your server's resources directory. You can organise scripts in sub-folders (e.g. resources/[swr]).
  4. 4Open server.cfg and add an ensure line for the resource. The name must match the folder name exactly.
  5. 5Save server.cfg and restart your server (or run ensure <resource-name> in the server console to hot-reload without a full restart).

Example server.cfg entry

ensure cfx-swr-cve-v2
Tip: If the resource fails to start, check the server console for errors. Missing dependencies listed in fxmanifest.lua are the most common cause.

Vending Machines

Vending Machine Script

Prop-triggered, NUI-based vending machines for FiveM. Players walk up to a machine, press E, and get a branded menu to purchase items. Includes custom audio, post-purchase animations, multiplayer proximity sound, and deep per-machine configurability from a single config.lua.

Requirements

  • FiveM server running artifact 2802 or newer (cerulean fx_version).
  • Lua 5.4 enabled (lua54 'yes' is set in the manifest).
  • An emote resource that registers /e <name> (compatible with rpemotes-reborn and dpemotes).
  • Framework: ESX (es_extended), QBCore (qb-core), or Standalone (no dependency).

Installation

  1. 1Drop the cfx-swr-vending folder into your server's resources directory.
  2. 2Add to server.cfg: ensure cfx-swr-vending.
  3. 3Open config.lua and set framework and money account.
  4. 4ESX / QBCore only: ensure item names in each machine's items table exist in your inventory.
  5. 5Restart the resource or reboot the server.
Config.Framework = 'esx'      -- or 'qb' or 'standalone'
Config.MoneyAccount = 'cash'  -- or 'bank'
No SQL. No imports. No additional dependencies beyond your framework and an emote resource.

Standalone / Custom Economy

When Config.Framework = 'standalone', the server fires a net event instead of touching any economy:

TriggerEvent('swr-vending:standaloneGive', source, itemName, price)

Listen to this event in your own resource to charge money and give the item however your server handles it.

Configuration Reference

All settings live in config.lua. The file is excluded from CFX asset escrow so it remains editable after purchase.

General

KeyDefaultDescription
Config.InteractDistance1.6Metres from a machine before prompt appears.
Config.InteractKey38GTA control index used to open menu (38 = E).
Config.Use3DPrompttrueShow floating 3D text when in range.
Config.PlayAnimationtruePlay post-purchase animations.
Config.PlayGrabAnimationtruePlay reach-and-grab animation before emote.
Config.EmoteCommand'e'Emote chat command prefix.
Config.Framework'standalone''esx', 'qb', or 'standalone'.
Config.MoneyAccount'cash''cash' or 'bank' for ESX/QBCore.
Config.PurchaseCooldown0Minimum ms between purchases per player; 0 disables.

Audio

KeyDefaultDescription
Config.AudioRadius20.0Radius for nearby players to hear purchase sound.
Config.PurchaseVolume0.5Volume (0.0 to 1.0) for purchasing player.
Config.NearbyMaxVolume0.5Max nearby volume with linear fade to 0 at edge.

Audio plays through NUI (browser Audio API). Place custom mp3 files in html/sound/ and register each file in fxmanifest.lua under files {}.

Prompt / 3D Text

KeyDefaultDescription
Config.PromptOffsetZ0.85Height above prop Z origin for prompt text.
Config.PromptKeyHint'[E]'Blue hint prefix; set empty string to hide.
Config.PromptFont4GTA font index.
Config.PromptScale0.34Prompt text scale.
Config.PromptTextColor{255,255,255,215}Text RGBA color.
Config.PromptBoxColor{0,0,0,150}Background box RGBA color; alpha 0 disables box.

Grab Animation (global defaults)

KeyDefaultDescription
Config.GrabAnimDict'mini@sprunk'Animation dictionary.
Config.GrabAnimClip'plyr_buy_drink_pt1'Clip name.
Config.GrabAnimDuration3500Milliseconds to hold the clip.
Config.GrabAnimBlendIn8.0TaskPlayAnim blend-in rate.
Config.GrabAnimBlendOut-8.0TaskPlayAnim blend-out rate.
Config.GrabAnimStopBlend3.0StopAnimTask blend-out speed.
Config.GrabFaceDuration800Duration for TaskTurnPedToFaceEntity.
Config.GrabFaceWait700Wait time while turn completes.

Scan / Detection Intervals

KeyDefaultDescription
Config.ScanInterval800ms between scans when no machine is near.
Config.NearbyInterval250ms between scans when machine is nearby.

Machine Configuration

Each entry in Config.Machines represents a brand. Add, remove, or rename brands freely. The resource auto-builds prop to brand lookup at startup.

Required fields

FieldDescription
propsList of model hashes (`prop_name`) used to identify this machine in world.
itemsList of { label, item, price } products shown in menu.

Appearance

FieldDescription
headerImage filename from html/images used as brand banner.
accentHex color string for machine theme in NUI menu.
subtitleText shown in menu header and world prompt.

Prompt overrides (optional)

FieldDescription
promptTextOverride 3D prompt text independent of subtitle.
promptHeightOverride Config.PromptOffsetZ for this machine only.
interactDistanceOverride Config.InteractDistance for this machine only.

Audio (optional)

FieldDescription
soundmp3 filename in html/sound; nil or omitted means no sound.

Choice screen (optional)

FieldDefaultDescription
showChoicetrueShows Drink it / Hold it screen after item pick.
defaultAction'hold'Used when showChoice is false: 'drink' or 'hold'.

Animations (optional)

FieldDescription
emotes{ drink = 'emoteName', hold = 'emoteName' }. Omitted keys do not fire emote.
emoteDelayDelay in ms after grab animation ends before emote runs.
grabAnimPer-machine override for dict, clip, duration, blendIn, blendOut, stopBlend.

Broadcast (optional)

FieldDescription
broadcastString or string table sent to global chat. %s becomes buyer name.

Full machine example

['mybrand'] = {
    props = { `prop_vend_soda_01` },
    items = {
        { label = 'My Drink', item = 'mydrink', price = 5 },
        { label = 'Diet Drink', item = 'dietdrink', price = 4 },
    },
    header = 'mybrand.png',
    accent = '#ff6600',
    subtitle = 'The best drink in Los Santos',
    promptText = 'Grab a drink',
    promptHeight = 1.0,
    interactDistance = 2.0,
    sound = 'vendingmachine.mp3',
    showChoice = true,
    defaultAction = 'drink',
    emotes = { drink = 'soda2', hold = 'soda' },
    emoteDelay = 0,
    grabAnim = {
        dict = 'mini@sprunk',
        clip = 'plyr_buy_drink_pt1',
        duration = 3500,
        blendIn = 8.0,
        blendOut = -8.0,
        stopBlend = 3.0,
    },
    broadcast = {
        '%s is thirsty!',
        '%s grabbed a drink.',
    },
},

Adding a new machine brand

  1. 1Create a brand image and place it in html/images/.
  2. 2Add a new entry to Config.Machines in config.lua.
  3. 3For custom sound, add mp3 to html/sound/ and register it in fxmanifest.lua under files {}.
  4. 4Ensure item names exist in your inventory system.
  5. 5Run ensure cfx-swr-vending in server console.

Adding custom audio

  1. 1Place mp3 files in html/sound/.
  2. 2Register each file explicitly in fxmanifest.lua.
  3. 3Set sound = 'mysound.mp3' on the machine entry in config.lua.
files {
    'html/sound/vendingmachine.mp3',
    'html/sound/coffee.mp3',
    'html/sound/mysound.mp3',
}

Multiplayer behavior: purchaser hears at Config.PurchaseVolume. Nearby players inside Config.AudioRadius hear with linear falloff.

Notifications

The script uses GTA native feed notifications. To replace with your framework notify, edit swr-vending:notify at the bottom of client.lua.

RegisterNetEvent('swr-vending:notify', function(message, kind)
    -- Replace this block with your framework notify export/event
    BeginTextCommandThefeedPost('STRING')
    AddTextComponentSubstringPlayerName(message)
end)

Troubleshooting

IssueChecklist
Menu does not openConfirm prop hash is listed in machine props, range is within interact distance, and config has no startup errors.
No sound playsVerify mp3 exists in html/sound and is explicitly listed in fxmanifest files, then inspect NUI DevTools for file/audio errors.
Nearby players do not hear soundIncrease audio radius and ensure nearby players have interacted at least once to prime NUI audio context.
Emote does not playStart emote resource before this resource, verify EmoteCommand and exact emote names, and account for emoteDelay.
Items not given or money not deductedValidate framework mode and item names against your inventory definitions; inspect framework errors in server console.
Cooldown not workingPurchase cooldown is milliseconds, memory-only, and resets on resource restart.
Grab animation looks wrongSet per-machine grabAnim overrides for non-soda props.
config.lua encrypted after escrowEnsure escrow_ignore includes config.lua in fxmanifest and re-upload through Tebex.

Net events reference

EventSideArgumentsDescription
swr-vending:purchaseServer receivebrand, itemIndexClient purchase confirmation.
swr-vending:purchaseSuccessClient receive-Fired on successful transaction to trigger animations.
swr-vending:notifyClient receivemessage, kindDisplays success or error notifications.
swr-vending:nearbySoundClient receivesoundFile, volumeInstructs nearby clients to play purchase audio.
swr-vending:standaloneGiveServer receivesource, item, priceStandalone handoff for external economy/inventory.

Clearing FiveM Cache

General Guide

Clearing your FiveM cache resolves texture glitches, loading failures, and outdated script errors that persist after a server update.

Steps

  1. 1Fully close FiveM. Open Task Manager and confirm no FiveM processes are still running.
  2. 2Press Win + R, paste the path below, and press Enter.
  3. 3Inside that folder, open FiveM.app then open the cache subfolder.
  4. 4Delete all subfolders and files inside cache. You can leave the empty cache folder itself in place.
  5. 5Relaunch FiveM. The cache will rebuild automatically on startup. This may take a few extra minutes on first load.

Cache folder path

%localappdata%\FiveM\FiveM.app\cache
Note: You do not need to delete priv or any folder outside of cache. Doing so may require you to re-link your FiveM account.

Updating Server Artifacts

General Guide

FiveM server artifacts are the core server binaries. Keeping them up to date ensures compatibility with new game builds and security patches.

Steps

  1. 1Visit the artifacts page for your OS and download the latest recommended build.
  2. 2Stop your FiveM server completely before making any file changes.
  3. 3Back up your current artifact files by copying them to a separate folder. This lets you roll back if something breaks.
  4. 4Extract the new artifact files into your server root directory, overwriting the existing binaries.
  5. 5Start your server and verify the updated version number appears in the server console output.
Windows:  https://runtime.fivem.net/artifacts/fivem/build_server_windows/master/
Linux:    https://runtime.fivem.net/artifacts/fivem/build_proot_linux/master/
Before updating: Review the Cfx.re changelog for the selected build. Occasionally artifact updates require config or resource changes to avoid breaking your server.

Fire Notify and Door Controller

Fire & EMS Station Tone-Out + Bay Door System · v2.0.0

Fire and EMS station tone-outs and bay doors for FiveM, built for Night ERS (with a standalone mode for SmartFires). When a player accepts a fire or EMS callout, the nearest station that serves that department tones out and its bay doors roll open. Crews run their stations day-to-day from an in-game station control tablet, opened with a command or from a door-controller keypad mounted on the station wall. Resource name: cfx-swr-fire-notify.

Features

  • Automatic tone-outs when a fire or EMS callout is accepted in Night ERS. Fire calls tone the nearest fire station, EMS calls the nearest EMS station, and a call that needs both departments tones both.
  • 3D positional audio. Tones play at the station itself, with distance fade and stereo panning, so everyone near the station hears them, not just the player who accepted the call.
  • Automatic bay doors. Doors at the toned station open on accept and stay open until someone closes them. Doors roll open and closed smoothly instead of snapping.
  • Station control tablet. Open and close bay doors, test tones, and set up the station in-game, with no file editing and no restarts.
  • Door-controller keypads. Place keypad props on station walls in-game. Crews use them with ox_target (default), qb-target, or a simple press-E prompt.
  • Everything is set up in-game. Stations, bay doors and keypads are built from the tablet, saved by the server, and survive restarts.
  • Shift-aware. In Night ERS mode, only players on an active fire or ambulance shift can work the doors and tones.
  • Secure by design. Every action is checked on the server. The admin password is verified server-side and can't be bypassed by editing the UI.
  • No required dependencies. Audio is handled by the script's own UI (no xsound needed), doors use GTA's built-in door system, and the tablet has no framework dependencies.
  • Pre-shift test tones, with an export so other scripts can trigger them.

Requirements

  • One of: Night ERS (for the ers, esx and qbcore modes) or SmartFires (for the standalone mode).
  • Recommended: ox_target, used by default for the door-controller keypads. Without it, keypads fall back to a press-E prompt, so it isn't required.
  • Optional: qb-target as an alternative to ox_target. ESX or QBCore if you want to restrict the tablet to certain jobs.

Installation

  1. 1Place the cfx-swr-fire-notify folder in your server's resources folder, for example resources/[local]/cfx-swr-fire-notify.
  2. 2Add it to server.cfg, after Night ERS (and ox_target, if you use it):
    ensure ox_target
    ensure night_ers
    ensure cfx-swr-fire-notify
    
    # Optional: give your admin group full tablet access without a password
    add_ace group.admin swr.firenotify.dev allow
  3. 3Open config.lua and at minimum: change Config.AdminPassword (it ships as password), check Config.Framework matches your setup (ers by default), and confirm the tone file names in Config.Departments.
  4. 4Start your server, join, and run /stationdoors to open the tablet.
  5. 5Set up your stations in-game (see Getting Started).
Keep the data/ folder. The server saves your stations, doors and keypads there and needs the folder to exist.

Getting Started

A fresh install starts with no stations, doors or keypads. Here's how to set up your first station.

  1. 1Unlock Admin. Open the tablet (/stationdoors), click Admin at the bottom of the menu, and enter your admin password. Players with the admin ace skip this step.
  2. 2Create the station. Stand on the station's apparatus floor (that's where tones will play from), go to Admin → Stations, type a name, tick Fire and/or EMS, and press Create here.
  3. 3Register the bay doors. Press Capture doors, aim at each bay door and fire an empty weapon at it. Then, under Awaiting setup, give each door its station and bay number and press Save. See Adding Bay Doors.
  4. 4Arrange the bays so the tablet shows them in the order they sit in the building. See Arranging the Station Layout.
  5. 5Place a keypad on the station wall so crews can open the tablet without a command. See Door-Controller Keypads.
  6. 6Test it. Run /teststationtones at the station, then accept a fire or EMS callout and watch the tones and doors fire.

Everything goes live for every player immediately. No restart needed.

How It Works

When a player accepts a callout in Night ERS, the script:

  1. 1Works out which departments the call needs. A call counts as fire or EMS if ERS marks it as requiring that unit (fireRequired / ambulanceRequired), or if its callout ID is in that department's list in the config.
  2. 2Finds the nearest station that serves each department. By default "nearest" means nearest to the player who accepted the call. Set Config.UseCalloutLocation = true to use the incident location instead.
  3. 3Plays that department's tone at the station. Every player within range hears it, with volume and stereo panning based on where they're standing and which way they're facing.
  4. 4Opens that station's bay doors for everyone (if Config.OpenDoorsOnAccept is on).

A short cooldown (Config.StationCooldown, 8 seconds by default) stops the same station from being toned repeatedly if several calls are accepted at once. The script just listens to Night ERS's callout-accepted event. No changes to Night ERS are needed.

Framework Modes

Set with Config.Framework.

ModeWhat triggers tones and doorsWho can use the tablet
'ers'Accepting a fire/EMS callout in Night ERS (default)Everyone can open it. Working doors and tones requires an active fire or ambulance ERS shift (see Shift Requirement).
'esx'Accepting a fire/EMS callout in Night ERSPlayers whose ESX job is in Config.AllowedJobs
'qbcore'Accepting a fire/EMS callout in Night ERSPlayers whose QBCore job is in Config.AllowedJobs
'standalone'Any SmartFires callout. No Night ERS needed.Everyone

Standalone mode plays the fire tone and opens doors at the station nearest to the fire itself the moment SmartFires spawns a fire callout. Set Config.SmartFiresAnyFire = true to also trigger on every fire SmartFires creates (player-lit, scripted, or spreading). The station cooldown is then the only thing limiting how often it fires.

The Station Control Tablet

Open it with /stationdoorsor by using a door-controller keypad. It opens on the station you're standing in and shows your distance to each one.

TabWho can see itWhat it does
Bay DoorsAnyone allowed to use the tabletOne tile per bay. Click a tile to open or close that door. Open or close every door at a station, or at every station at once. Door states are live and shared by all players.
TonesAnyone allowed to use the tabletPlay the test tone, or preview the real fire or EMS tone, at any station. Tells you whether you're within hearing range.
AdminPassword or admin aceCreate and manage stations, place keypads, and capture, arrange and edit bay doors.
DebugAdmins onlyLive audio status, each tone's volume and panning, distances to every station, and the full door list. Hidden until Admin is unlocked.

Permissions

The script asks three separate questions about every player.

QuestionRule
OpenCan they see the tablet?Everyone in ers and standalone modes. In esx and qbcore modes, only jobs listed in Config.AllowedJobs.
OperateCan they open doors and play tones?In ers mode, they must be on an active fire or ambulance shift. Otherwise, the same as Open.
AdminCan they change the setup?Admin password, admin ace, or a listed identifier.

All three are checked on the server, so they can't be faked from the player's game.

Getting admin access. There are three ways to grant admin:

  1. 1The admin password. Click Admin in the tablet and enter Config.AdminPassword. Access lasts until the player disconnects. Five wrong attempts locks that player out for 30 seconds.
  2. 2The admin ace. Players with the ace skip the password entirely. The ace name can be changed with Config.DevAce.
    add_ace group.admin swr.firenotify.dev allow
  3. 3Identifiers. List specific players' identifiers (license, steam, discord or fivem) in Config.DevIdentifiers.

To turn off password entry and allow only the ace and identifiers, set Config.AdminPassword = ''.

Building solo? Config.DevOpenToAll = true gives every player admin access while you set up your stations. Never leave this on for a live server.

Shift Requirement (ERS Mode)

With Config.ErsRequireShift = true (the default), a player must be on shift in Night ERS as fire or ambulance to open or close bay doors or play tones. Police, tow and off-duty players are refused.

  • Off-shift players can still open the tablet. It shows a banner explaining why the controls are disabled. This also means an off-duty admin can always reach the admin password prompt.
  • Admins bypass the shift requirement, so stations can be set up before anyone is on duty.
  • If Night ERS isn't running, the check is skipped, so a missing dependency never locks crews out of their own doors.
  • The tablet re-checks every few seconds, so going on shift unlocks the controls without closing and reopening it.

Change which ERS services count with Config.ErsAllowedServices (valid values: 'police', 'ambulance', 'fire', 'tow'), or turn the requirement off with Config.ErsRequireShift = false.

Stations

A station is where tones play and which bay doors belong together. Manage them under Admin → Stations.

ActionHow
CreateStand where tones should play (usually the apparatus floor), type a name, tick Fire and/or EMS, and press Create here. The position is taken from where you're standing.
RenameClick the name, edit it, and press Enter.
Fire / EMSDecides which department's tones the station answers. A station with neither ticked never tones.
Move hereMoves the station's tone position to where you're standing.
FindPuts a waypoint on the station.
DeleteClick twice to confirm.

A station can't be deleted while it still has bay doors. Move or delete its doors first. Each station has a permanent ID number. IDs are never reused or renumbered, so deleting station 2 leaves station 3 as station 3, and no doors or keypads get reassigned by accident.

Bay Doors

Bay doors open automatically at the toned station when a callout is accepted and stay open until someone closes them. Doors never open or close on their own just because a player walks or drives near them. Door states are shared with every player, including anyone who joins later. When not open, doors are held closed and locked.

Adding Bay Doors

  1. 1Open the tablet, unlock Admin, and press Capture doors. The tablet closes and capture mode starts. (/registerdoors does the same thing.)
  2. 2Aim at a bay door. It's outlined in orange when it's under your crosshair.
  3. 3Fire an empty weapon (0 ammo) at it. Loaded weapons are refused, so no real shots go off. The door is captured and set aside for setup.
  4. 4Repeat for the rest of the bays. Press X, or just reopen the tablet, to stop capturing.
  5. 5Under Admin → Awaiting setup, give each door its station, bay number and a label, then press Save. It goes live for every player right away.

Doors that are already registered are recognised, so you can't create duplicates by accident. Doors can be captured from up to about 40 metres away.

Arranging the Station Layout

Bay numbers often don't match the order the doors physically sit in. Bay 1 is not always the leftmost door. Admin → Station layout & names fixes this.

  • Drag a bay tile to move it.
  • Click a bay's name to rename it (press Enter or click away to save).

This only changes the order of the tiles on the Bay Doors tab. It never changes a bay number, which physical door is which, or what /openbay opens. The layout is shared by every player and survives restarts.

Editing Bay Doors

Admin → Registered doorslists every door. You can change each door's station, bay number and open ratio right in the list. Find puts a waypoint on the door, and Delete removes it (click twice to confirm). The open ratio sets how far, and in which direction, a door opens:

ValueResult
1.0Fully open (default)
0.5Half open
-1.0Fully open, in the opposite direction

If a door opens the wrong way, flip its ratio (for example 1.0 → -1.0). Values closer to zero than 0.1 are rounded up to 0.1.

Door Movement

Bay doors roll open and closed over Config.DoorTravelSeconds(3 seconds by default) instead of snapping into place. Doors snap straight to their correct position, without rolling, when you join the server, after a resource restart, and after a door is edited in the tablet. That way joining the server doesn't roll every bay door open in front of you.

Use this script as the only door script for station bay doors. If another door-lock script also controls the same doors, the two will fight over them.

Door-Controller Keypads

Keypads are props mounted on station walls that open the tablet, so crews don't need to type a command. You can place as many as you like, at as many stations as you like. How players use a keypad depends on Config.TargetSystem:

SettingHow players use it
'ox_target'(default) Hold your target key (Alt by default), look at the keypad, and click Open Door Controller.
'qb-target'The same, using qb-target.
'prompt'Walk up to the keypad and press E. No target script needed.
'auto'Uses ox_target if it's running, otherwise qb-target, otherwise the prompt.

If the target script you chose isn't running, keypads automatically fall back to the press-E prompt and a message is printed to the client console. In prompt mode, players can rebind the key in Settings → Key Bindings → FiveM → "Use station door controller". Config.ControllerKey sets the default key. A keypad opens the tablet on its station.

Placing a Keypad

  1. 1Open the tablet, unlock Admin, and go to Door controllers.
  2. 2Press Place a controller. The tablet closes and a see-through preview of the keypad follows your crosshair, sitting flat against whatever wall you look at.
  3. 3Place it with the controls below. The keypad goes live for every player as soon as it's placed.
ControlAction
Left clickPlace the keypad
Right clickCancel
Scroll wheelRotate the keypad on the wall
XReset the rotation

The station is assigned automatically.A new keypad belongs to the nearest station, so it opens the tablet on the building you're standing in. If there's no station within 150 metres, the keypad isn't assigned to one and just opens the tablet. You can change a keypad's station afterwards from the dropdown in the Door controllers list, where you can also Find (waypoint) or Removeeach keypad. A keypad is not tied to specific doors. It's just a way to open the tablet.

Using a building's existing keypad. If your station MLO already has a keypad model on the wall, you can use it instead of spawning a second one:

  1. 1Place a keypad in the same spot as the MLO's keypad.
  2. 2Stop the server.
  3. 3Open data/controllers.json, find that keypad's entry, and add "spawn": false.
  4. 4Start the server. The interaction stays, but no extra prop is spawned.

Using a different keypad prop. Change Config.ControllerModel to any prop name. If keypads with your prop end up facing into the wall, set Config.ControllerYawOffset to 180.0 (or try 90.0 / -90.0). Keypads that were already placed keep their old rotation, so re-place them after changing this.

Tones and Audio

Adding your own tones:

  1. 1Export your tone from an audio editor such as Audacity (File → Export → Export as OGG). .ogg is recommended; .mp3 and .wav also work.
  2. 2Use a lowercase file name with no spaces, for example fd-tone.ogg.
  3. 3Put the file in the resource's html/sounds/ folder.
  4. 4List it in config.lua:
    Config.Departments.fire.tones = { 'fd-tone.ogg' }
    Config.Departments.ems.tones  = { 'ems-tone.ogg' }

List several files for a department to play a random one each time (Config.RandomTone). The script ships with fd-tone.ogg (fire), ems-tone.ogg (EMS), test-tone.ogg (test tone) and diag.mp3 (a known-good backup test sound).

All tones are positional: they play at the station and get quieter with distance. The settings that control this:

SettingDefaultWhat it does
Config.Volume0.7Volume at the station (0.0 to 1.0).
Config.FullVolumeDistance8.0Within this many metres, the tone plays at full volume.
Config.AudibleDistance60.0Beyond this many metres, the tone can't be heard.
Config.FalloffExponent2.0How quickly the tone fades. 1.0 fades evenly; higher values drop off faster, which usually sounds more natural. Try 1.6 to 2.5.

Test Tones (Pre-Shift Check)

Run /teststationtones at your station. It plays Config.TestTone at your nearest station exactly like a real tone, so everyone nearby hears it, and the chat tells you which station toned and how far away you are. Walk around and turn your head to hear the fade and panning. You can also play the test tone, or preview the real fire and EMS tones, at any station from the tablet's Tones tab.

Commands

CommandWhat it doesWho can use it
/stationdoorsOpen the station control tabletAnyone allowed to open the tablet
/teststationtonesPlay the test tone at your nearest stationAnyone allowed to operate
/openbay [bay]Open one bay door at your nearest stationAnyone allowed to operate
/closebay [bay]Close one bay door at your nearest stationAnyone allowed to operate
/openallstationdoorsOpen every door at your nearest stationAnyone allowed to operate
/closeallstationdoorsClose every door at your nearest stationAnyone allowed to operate
/registerdoorsStart or stop door capture modeAnyone, but only admins can save captured doors
/stoptonesInstantly silence all tones for yourselfEveryone
/tonesdiagPrint audio troubleshooting info to the F8 consoleEveryone

[bay]is the door's bay number set in the tablet. All main command names can be changed in config.lua (Config.TabletCommand, Config.TestCommand, Config.OpenBayCommand, Config.CloseBayCommand, Config.OpenAllCommand, Config.CloseAllCommand, Config.DoorRegisterCommand). Enter them without the leading slash.

Configuration Reference

All settings live in config.lua. Stations, bay doors and keypads are notin the config. They're set up in-game and saved in the data/ folder.

General, framework and mode

SettingDefaultDescription
Config.EnabledtrueMaster switch. false turns off tone-outs and automatic door opening.
Config.DebugfalsePrints detailed setup and troubleshooting info to the server console and F8.
Config.Framework'ers''ers', 'esx', 'qbcore' or 'standalone'.
Config.AllowedJobs{ 'fire', 'ambulance' }esx / qbcore only: jobs allowed to use the tablet and commands.
Config.ErsRequireShifttrueers only: require an active ERS shift to work doors and tones.
Config.ErsAllowedServices{ 'fire', 'ambulance' }Which ERS shift types count.
Config.ErsResource'night_ers'Folder name of your Night ERS resource.
Config.SmartFiresAnyFirefalsestandalone only: trigger on every SmartFires fire, not just its callouts.

Departments and tones

SettingDefaultDescription
Config.Departments.fire.tones{ 'fd-tone.ogg' }Fire tone file(s) in html/sounds/.
Config.Departments.ems.tones{ 'ems-tone.ogg' }EMS tone file(s) in html/sounds/.
Config.Departments.<dept>.requiredFlag'fireRequired' / 'ambulanceRequired'The Night ERS "unit required" flag that marks a call as this department.
Config.Departments.<dept>.calloutIdsPre-filledNight ERS callout IDs that count as this department. Add your own custom callouts here.
Config.RandomTonetruePick a random tone when a department lists more than one.
Config.OnlyRequiredFlagfalsetrue = match departments only by ERS's required flags and ignore the callout ID lists.
Config.UseCalloutLocationfalsetrue = tone the station nearest the incident instead of the player who accepted.
Config.AcceptEvent'ErsIntegration::OnAcceptedCalloutOffer'The Night ERS event the script listens for. Only change this if Night ERS renames it.

Audio

SettingDefaultDescription
Config.Volume0.7Volume at the station (0.0 to 1.0).
Config.AudibleDistance60.0Metres from the station at which the tone fades to silence.
Config.FullVolumeDistance8.0Metres from the station within which the tone is at full volume.
Config.FalloffExponent2.0Fade curve. 1.0 = even; higher = faster drop-off.
Config.VolumeRefreshMs120How often (ms) volume and panning update as players move.
Config.MaxToneSeconds60Safety limit on tone length. The tone's real length is used once it's known.
Config.StationCooldown8Seconds before the same station and department can tone again. 0 turns it off.
Config.TestTone'test-tone.ogg'File used for test tones.
Config.TestCommand'teststationtones'Test tone command name.

Bay doors

SettingDefaultDescription
Config.OpenDoorsOnAccepttrueOpen the toned station's bay doors when a callout is accepted.
Config.DoorTravelSeconds3.0Seconds a door takes to fully open or close. 0.1 is effectively instant.
Config.TabletCommand'stationdoors'Command that opens the tablet.
Config.DoorRegisterCommand'registerdoors'Command that toggles door capture mode.
Config.OpenBayCommand'openbay'Command to open one bay.
Config.CloseBayCommand'closebay'Command to close one bay.
Config.OpenAllCommand'openallstationdoors'Command to open all bays at the nearest station.
Config.CloseAllCommand'closeallstationdoors'Command to close all bays at the nearest station.

Door-controller keypads

SettingDefaultDescription
Config.ControllerModel'reh_prop_reh_keypad_01a'Prop used for keypads.
Config.ControllerYawOffset0.0Rotation fix if a custom prop faces into the wall. Try 180.0.
Config.TargetSystem'ox_target''ox_target', 'qb-target', 'prompt' or 'auto'.
Config.ControllerKey'E'Default key for the prompt mode. Players can rebind it.
Config.ControllerLabel'Open Door Controller'Text shown on the target option or prompt.
Config.ControllerIcon'fas fa-tablet-screen-button'Icon shown in ox_target / qb-target.
Config.ControllerDistance2.0How close (in metres) a player must be to use a keypad.

Admin access

SettingDefaultDescription
Config.AdminPassword'password'Password for the tablet's Admin tab. Change this. '' disables password entry.
Config.DevAce'swr.firenotify.dev'Ace permission that grants admin without a password.
Config.DevIdentifiers{}Player identifiers that get admin without a password.
Config.DevOpenToAllfalseGives every player admin. For solo setup only. Never use on a live server.

Developer Integration

Trigger a test tone from another script. Useful for shift-readiness or sign-on systems.

-- Server side
-- Test tone at the player's nearest station
exports['cfx-swr-fire-notify']:TestStationTones(playerId)

-- Test tone at a specific station (by station ID)
exports['cfx-swr-fire-notify']:TestStationTones(playerId, stationId)

-- Client side (runs the same permission checks as /teststationtones)
TriggerServerEvent('swr-fire-notify:testStationTones')

The export returns true if the tone played. Calls through the export are trusted and skip the job and shift checks. If you renamed the resource folder, use your folder name in exports[...].

Events the script listens to

EventSourceUsed in
ErsIntegration::OnAcceptedCalloutOfferNight ERSers, esx, qbcore modes
Server:newAutomaticFireSmartFiresstandalone mode
Server:newFireEventSmartFiresstandalone mode with Config.SmartFiresAnyFire = true

For the shift requirement, the script calls these Night ERS server exports:

exports['night_ers']:getIsPlayerOnShift(source)
exports['night_ers']:getPlayerActiveServiceType(source)

Saved Data, Backups and Resets

Stations, bay doors and keypads are saved by the server in the resource's data/ folder:

FileContains
data/stations.jsonStations: name, tone position, fire/EMS
data/doors.jsonBay doors: station, bay number, label, layout order, open ratio
data/controllers.jsonDoor-controller keypads: position, rotation, station
  • Each file is created automatically the first time you add something.
  • To back up or move your setup to another server, stop the server and copy all three files.
  • To reset stations, doors or keypads, stop the server and delete that file.
  • Don't edit these files while the server is running. The server will overwrite your changes.
  • If a file gets corrupted, the server keeps a copy named like doors.json.corrupt-<timestamp> before starting fresh, so the data can still be recovered by hand.

What You Can Edit

The script is protected by Cfx.re Asset Escrow. These parts stay open and editable; the Lua scripts are encrypted.

  • config.lua: every setting
  • html/: the tablet's look and layout, and the tone sound files
  • data/: your saved stations, doors and keypads
  • README.md
Adding your own CSS or JS files to the tablet? Each one must be listed individually in files { } in fxmanifest.lua, or it won't load.

Updating

Version 2.0.0 no longer reads Config.Stations, Config.Doors or Config.Controllers from the config. Stations, bay doors and keypads are now set up in-game and saved in data/.

From 1.1.0 to 2.0.0

  1. 1Back up your existing config.lua and your data/ folder.
  2. 2Replace the resource with the new version. Keep your data/doors.json, and copy your tone settings, framework mode and any other settings you changed into the new config.lua.
  3. 3Set Config.AdminPassword, and optionally add the admin ace to server.cfg.
  4. 4Start the server and re-create your stations from the tablet in the same order as your old Config.Stations list. Your bay doors are saved against station numbers 1, 2, 3 and so on, in that order, so they reattach to the right stations automatically.
  5. 5Re-place your keypads from the tablet. Keypads listed in the old Config.Controllers are not carried over.

From 1.0.x to 2.0.0: follow the 1.1.0 steps above, then also register your bay doors from the tablet (see Adding Bay Doors). Keep your old config handy as a reference for which doors belong to which station. Tones, the Night ERS and SmartFires hooks, and all the old door commands work as before.

Future updates: your stations, doors and keypads live in data/, so keep your data/ folder (and your config.lua changes) when replacing the resource with a new version.

Troubleshooting

IssueFix
No sound at allCheck that the file names in Config.Departments and Config.TestTone match the files in html/sounds/ exactly, including the extension. A missing or broken file prints a tone "..." failed message in F8. Run /tonesdiag; the audio engine should report running.
A tone seems silentYou're probably outside Config.AudibleDistance of the station. All tones are positional; walk closer.
The wrong callouts trigger tonesAdjust the calloutIds lists in Config.Departments, or set Config.OnlyRequiredFlag = true to rely only on Night ERS's fire/ambulance "required" flags.
A station tones for the wrong departmentFix its Fire / EMS tick boxes under Admin → Stations.
The fade sounds too gradual or too suddenRaise or lower Config.FalloffExponent, and adjust Config.FullVolumeDistance.
A bay door won't moveRe-capture it, aiming at the middle of the door panel so the actual door object is captured. Make sure no other door-lock script is controlling the same door.
A door opens the wrong wayFlip its open ratio under Admin → Registered doors (for example 1.0 → -1.0).
A door snaps open instead of rollingThat's expected when joining, after a restart, or after editing a door. Doors that are opened or closed normally always roll.
Door capture says "No door object hit"Aim at the middle of the door panel (not the frame or wall), stay within about 40 metres, and fire again.
Door capture refuses to workYour weapon must be empty (0 ammo).
Alt / ox_target does nothing on a keypadMake sure ox_target is started before this script in server.cfg, and that Config.TargetSystem is 'ox_target'. Check F8 for a message saying keypads fell back to the prompt. Turn on Config.Debug to draw the keypad's target area in the world.
A keypad faces into the wallOnly happens with a custom prop. Set Config.ControllerYawOffset = 180.0, then remove and re-place the keypad.
The tablet is blank, or unstyled text covers the screenThe tablet's files aren't loading. Run refresh then ensure cfx-swr-fire-notify in the server console. If you added your own CSS or JS files, make sure each one is listed in fxmanifest.lua.
Doors and tones refuse with an "on shift" messageThat's the shift requirement. Go on shift as fire or ambulance in Night ERS, or set Config.ErsRequireShift = false.
The admin password is rejectedCheck Config.AdminPassword in config.lua. After five wrong attempts the player is locked out for 30 seconds.
Nothing is saved after a restartMake sure the data/ folder exists inside the resource and the server can write to it.

First-Time Setup Tip

Set Config.Debug = true, accept a fire or EMS callout, and watch the server console. It shows exactly what Night ERS sent, which departments matched, and which station was chosen. Turn it off again once everything works.

Changelog

2.0.0

  • Stations, bay doors and keypads are set up entirely in-game and saved in data/. The Config.Stations, Config.Doors and Config.Controllers lists are gone.
  • Station management in the tablet: create, rename, move and delete stations, and choose which departments each one answers.
  • In-game keypad placement with a live preview that mounts flush to walls. New keypads are assigned to the nearest station automatically.
  • ox_target by default for keypads, with qb-target or a rebindable press-E prompt as alternatives.
  • Password-protected Admin section, plus ace and identifier access.
  • Drag-to-arrange station layouts and in-place renaming.
  • Bay doors now roll open and closed (Config.DoorTravelSeconds).
  • Doors can open in reverse with a negative open ratio.
  • ers mode can require an active fire/EMS shift (Config.ErsRequireShift).
  • Corrupted data files are backed up instead of overwritten.

1.1.0

  • In-game station control tablet (/stationdoors).
  • Bay doors registered in-game by capture and saved in data/doors.json.
  • Door-controller keypads, configured in config.lua.

1.0.x

  • Fire and EMS tone-outs on Night ERS callout accept, with 3D positional audio.
  • Automatic bay doors at the toned station.
  • Standalone SmartFires mode.

Commercial Vehicle Enforcement V2

CVSA Inspections, Scales & Out-of-Service Orders · v2.0.0

CVE V2 brings commercial vehicle enforcement to your FiveM server. Officers run CVSA roadside inspections on trucks and trailers, weigh them with wheel-load scales or at weigh stations, and place drivers and vehicles out of service, all from an in-hand tablet. It runs standalone on any server: no framework, no database, nothing else to install. Resource name: cfx-swr-cve-v2.

Features

Inspections

  • Three CVSA inspection levels: Level I full inspection, Level II walkaround, Level III driver and credentials.
  • Walk the rig and inspect each component: brakes, tires, lights, coupling, cargo securement, placards and more. Each one comes back Pass, Minor, Major or Out of Service with FMCSA-referenced finding text.
  • Tractor and trailer are inspected together as one combination.
  • A live report builds as you go and is filed once you write your narrative.
  • Every officer keeps a full history of their reports.

Weight enforcement

  • PortaScales. Set a wheel-load pad under each wheel (optionally carried from your scale truck) and read every axle on the tablet.
  • Weigh stations. Fixed platforms that weigh the whole rig as it pulls on.
  • Overweight steer, single, tandem and gross readings become citable violations, graded Minor, Major or Out of Service by how far over they are.

Out of service

  • Place a driver (by name) or a truck or trailer (by plate) out of service, with a reason.
  • Look up any driver or plate on a later stop. Orders stay active until an officer lifts them.
  • With night_ers, every traffic stop checks the driver and plate automatically and alerts the officer.

For admins

  • Set up Discord logging in-game. Webhook URLs never reach players.
  • Position inspection points on any vehicle from the tablet, live.
  • 35 trucks and 22 trailers configured out of the box, plus any you add.
  • Built-in highway truck traffic so there's always something to inspect.
  • Configs stay fully editable after purchase.

Requirements

  • Required: a FiveM server on a recent artifact.
  • Optional: night_ers, for automatic out-of-service checks on traffic stops.
  • Optional: ox_target, for target-eye interaction with PortaScales.
No framework. No database. ESX, QBCore, SQL and companion resources are not needed.

Installation

  1. 1Download the resource from your Cfx.re Keymaster.
  2. 2Put the cfx-swr-cve-v2 folder in your server's resources folder. Keep the folder name as it is.
  3. 3Add it to server.cfg. If you use night_ers or ox_target, ensure them before this line:
    ensure ox_target
    ensure night_ers
    ensure cfx-swr-cve-v2
  4. 4Complete the first-time setup, then restart the server.

First-Time Setup

All settings are in the config folder. Set these four things before going live.

1. Set a Dev Tools password in config/script_config.lua:

DevToolsPassword = 'choose-a-strong-password',

Dev Tools are where admins position inspection points, place weigh stations and set up Discord. They are off until you set a password. You can set it in server.cfg instead: set cve_dev_password "…".

2. Set your agency in config/script_config.lua:

Inspector = {
    Agency      = 'San Andreas DOT',
    BadgePrefix = 'SADOT-',
},

3. Optional: a scale truck for PortaScales. Out of the box, officers place PortaScales straight under a wheel. If your server has a patrol vehicle that should carry the scales, turn on carrying in config/scale_config.lua and list its spawn name(s):

Carry = {
    Enabled        = true,
    SourceVehicles = { 'your_scale_truck' },

Officers then take each pad from the back of that vehicle and walk it to the wheel.

4. Connect Discord. In game, open the tablet (/cve) → Dev Tools → enter your password → Configure Discord. See Discord Logging.

Officer Guide: The Tablet

Type /cve to take out the tablet. You can also bind it to a key under Settings → Key Bindings → FiveM.

AppUse it to
InspectorStart an inspection, follow your progress, place a unit out of service
Past ReportsRead your filed reports and edit narratives
Inspector ProfileSet your name, badge and agency
FMCD LookupSearch, place and lift out-of-service orders
ScalesRead PortaScales and weigh stations
Dev ToolsAdmin tools (password)

Your inspector profile. Open Inspector Profile and enter your display name and badge number. They appear on every report and Discord post, and are remembered between sessions.

Running an Inspection

  1. 1Stop the truck and open Inspector.
  2. 2Pick a level: Level I covers everything, including under the vehicle. Level II is a walkaround: tires, lights, cargo, placards. Level III is the driver, cab and credentials only.
  3. 3Press Start Inspection. The tablet goes away and orange markers appear on the truck and trailer.
  4. 4Walk to each marker and press E. Each component comes back Pass, Minor, Major or Out of Service.
  5. 5When every marker is done, open /cve again, write your narrative, and file the report.

The report isn't filed until you write the narrative, and the tablet reminds you if one is waiting. You can edit the narrative later from Past Reports.

Weighing with PortaScales

  1. 1Stand next to a wheel and place a pad (/placeportascale, the E prompt, or ox_target). If your server uses a scale truck, take the pad from the back of it first (/getportascale) and walk it to the wheel.
  2. 2Repeat for each wheel you want to weigh.
  3. 3Open Scales. Each pad shows its live weight, grouped by truck and trailer, with power, zero and AccuSet controls.
  4. 4With an inspection running, press Send to inspection to add a reading to your report. An overweight axle is added as a violation automatically.
  5. 5Pick pads back up with /pickupportascale. With a scale truck, return an unused one with /returnportascale.
AxleDefault legal limit
Steer axle12,000 lb
Single axle20,000 lb
Tandem34,000 lb
Gross80,000 lb

Up to 1,000 lb over is Minor, 1,000 lb or more is Major, and 5,000 lb or more is Out of Service.

Weigh Stations

Weigh stations are fixed platforms your admins have placed. Have the truck pull on, then open Scales → Weigh Stations and pick the station. It shows the gross weight of the truck and trailer on it. Press Send to inspection to check it against the 80,000 lb gross limit.

Out-of-Service Orders

An out-of-service order records that a driver or vehicle may not operate until the problem is fixed. It goes on file for every officer to see on later stops. A reason is always required.

TaskHow
Place an order on a vehicleDuring an inspection: Inspector → the unit's card → Place Out of Service. Any time: FMCD Lookup → Place out of service → Tractor / trailer → enter the plate.
Place an order on a driverFMCD Lookup → Place out of service → Driver → first and last name. Date of birth and the plate they were driving are optional.
Look someone upFMCD Lookup → search by Vehicle plate or Driver name. Results say clearly when the driver or vehicle is currently out of service, and show the full history.
Lift an orderFind the order in FMCD Lookup → Lift order… → give the reason (repairs verified, rest break completed, …) → Confirm lift. The order stays on file marked Lifted, with who lifted it and why.

ERS Traffic Stops

If your server runs night_ers, CVE checks every traffic stop for you:

  • When you pull a vehicle over, the driver's name and the plate are checked against active out-of-service orders. If either is out of service you get an OUT OF SERVICE alert on screen with the reason, and FMCD Lookup shows a red banner.
  • FMCD Lookup shows the current stop's driver and plate, with buttons to look either one up or place a new order with the details already filled in.

Admin Guide: Dev Tools

Open /cve → Dev Tools and enter the Dev Tools password. The unlock lasts for your session.

ToolWhat it's for
Edit Tractor / Trailer NodesPosition the inspection points on a vehicle
Saved ConfigsReview, edit, copy to other models, or delete saved layouts
PermaScalesPlace, rename, teleport to and delete weigh stations
Configure DiscordWebhooks and what gets posted
Traffic ToolsTurn highway truck traffic on or off, see and clear active trucks
Bake configsWrite saved layouts into the config files
DebuggingLive debug overlays and logging
Prop testerSpawn the CVE props locally to check them

Dev Tools also unlock two PortaScale testing tools: a weight override on each reading, and /clearportascales, which removes every pad on the server.

Adding or Adjusting Vehicles

Every vehicle that can be inspected is listed in config/tractor_config.lua (trucks, buses, equipment) or config/trailer_config.lua (trailers). To add one:

  1. 1Copy an existing entry of a similar shape and change the model name and label.
  2. 2Restart the resource.
  3. 3In game, park next to the vehicle, open Dev Tools → Edit Tractor / Trailer Nodes, and drag each inspection point into place. Save pushes the new layout to every player immediately.

Each vehicle also has an empty weight (and trailers a cargo range) in the same files, which drives the scale readings.

Saved layouts are kept by the server. To write them into the config files, so they survive a data wipe or can be copied to another server, use Dev Tools → Bake configs (or cvebakeconfigs in the server console).

Placing Weigh Stations

  1. 1Dev Tools → PermaScales, type a name (e.g. Route 68 Weigh Station), then press 3D preview placement.
  2. 2A ghost platform follows your view. Q/E or scroll to rotate, R/F to raise or lower, G to toggle ground snap, SHIFT for bigger steps.
  3. 3ENTER to place, BACKSPACE to cancel.
  4. 4It appears for every player, now and after every restart. Rename, teleport to or delete it from the same list.

Any configured truck or trailer within 14 m of the platform's centre is weighed.

Discord Logging

  1. 1Dev Tools → Configure Discord.
  2. 2Paste a webhook URL for each channel, or paste one and press Use for both channels.
  3. 3Press Send test to check it.
  4. 4Choose what gets posted:
ChannelPosts
LogsEvery component as it's inspected (high volume)
Report logsFiled reports, narratives, and out-of-service orders placed and lifted

You can post each report as soon as it's filed, or hold it until the narrative is written and post one combined message.

Keep webhooks out of script_config.lua. Webhooks set from Dev Tools are stored on the server and never sent to players. script_config.lua is readable by every connected player.

Highway Traffic

CVE can keep AI semi-trucks driving on the highways near players, so officers always have something to inspect. Turn it on or off live in Dev Tools → Traffic Tools, and tune spawn rates, distances and which trucks and trailers appear in config/traffic_config.lua.

Configuration Reference

All files are in config/ and stay editable after purchase.

FileWhat's in it
script_config.luaGeneral settings, keybinds, notifications, inspection outcomes and finding text, agency, passwords, night_ers, Discord
tractor_config.luaTrucks, buses and equipment: inspection points and weights
trailer_config.luaTrailers: inspection points, weights and cargo ranges
scale_config.luaWeight limits, PortaScale and weigh-station settings, interaction methods, scale truck
traffic_config.luaHighway truck traffic

Common Settings

SettingFileDefaultDescription
DevToolsPasswordscript''Dev Tools password. Off until set.
RequireMenuPassword / MenuPasswordscriptfalse / ''Password to open the tablet at all.
Inspector.Agency / BadgePrefixscriptSan Andreas DOT / SADOT-Shown on reports.
Notify.Stylescript'tablet'tablet, native or chat notifications.
AllowReinspectionscripttrueAllow repeat inspections of the same truck.
Outcomesscript50 / 30 / 15 / 5Chance weighting of Pass / Minor / Major / OOS.
ERS.Enabled / ERS.AlertOnStopscripttrue / truenight_ers checks and alerts.
WeightLimitsscale12k / 20k / 34k / 80k lbSteer / single / tandem / gross limits.
OverageThresholdsscalemajor = 1000, oos = 5000lb over the limit for Major / OOS.
Interaction.Methodsscale{ 'command', 'ox_target' }Add 'keybind' for a "Press E" prompt.
PortaScale.Carry.Enabled / SourceVehiclesscalefalseCarry pads from a scale truck, and its spawn name(s).
PermaScale.DetectionRadiusscale14.0How close to the platform centre a rig must be.
Enabled / MaxActiveTruckstraffictrue / 6Highway traffic on/off and cap.

server.cfg Overrides

set cve_dev_password            "…"
set cve_menu_password           "…"
set cve_discord_log_webhook     "https://discord.com/api/webhooks/…"
set cve_discord_report_webhook  "https://discord.com/api/webhooks/…"

Integrations & Exports

night_ers. Works automatically when night_ers is running (see ERS Traffic Stops). Nothing to install or edit in ERS. With the ERS MDT active, driver names come from your MDT's civilian records, so the same person matches from stop to stop.

ox_target. Add 'ox_target' to Interaction.Methods in scale_config.lua(on by default). If ox_target isn't running, CVE uses the other methods in the list.

Server exports. Ask CVE whether a driver or vehicle is out of service from any other resource: an MDT, a job script, a dispatch system.

local isOos, orders = exports['cfx-swr-cve-v2']:IsDriverOutOfService('John', 'Doe')
local isOos, orders = exports['cfx-swr-cve-v2']:IsVehicleOutOfService('ABC 123')

if isOos then
    print(('OUT OF SERVICE: %s (by %s, %s)'):format(orders[1].reason, orders[1].inspector, orders[1].serverTsHuman))
end

orders lists the active orders, newest first. Names and plates match regardless of capitalisation and spacing.

Commands

CommandWhoDescription
/cveEveryoneOpen the tablet
/getportascaleEveryoneTake a PortaScale from the scale truck (carrying on)
/placeportascaleEveryonePlace it under the nearest wheel
/pickupportascaleEveryonePick up the nearest PortaScale
/returnportascaleEveryoneReturn a carried PortaScale to the truck (carrying on)
/portascaledebugEveryoneShow which wheels can take a pad
/clearportascalesDev ToolsRemove every PortaScale
/placepermascale [name]Dev ToolsPlace a weigh station
cvebakeconfigsServer console / Dev ToolsSave inspection-point layouts into the config files

The PortaScale command names can be changed in scale_config.lua. The officer commands are only available while 'command' is in Interaction.Methods.

FAQ & Troubleshooting

IssueFix
Dev Tools say they're disabledSet DevToolsPassword in config/script_config.lua (or the cve_dev_password convar) and restart.
I can't take a PortaScale from my truckCarrying is off by default, so just place pads with /placeportascale. If you turned it on, add your vehicle's spawn name to PortaScale.Carry.SourceVehicles in scale_config.lua and stand behind it.
No markers appear on a truckThe model has to be listed in tractor_config.lua or trailer_config.lua. Add it, then position its points with the node editor.
A weigh station reads 0Only configured trucks and trailers are weighed, and the rig has to be within 14 m of the platform's centre. Turn on Dev Tools → Debugging → PermaScale markers to check the station spawned correctly.
Nothing posts to DiscordCheck Dev Tools → Configure Discord: a URL is saved, Send test works, and the event you expect is ticked. EnableLogging must be true.
ERS stops don't alertnight_ers must be started before CVE, ERS.Enabled must be true, and the order must be active (not lifted). Driver names have to match the first and last name ERS reports for that ped.
Will updates wipe my data?No. Reports, out-of-service orders, weigh stations, saved layouts and Discord settings are stored by the server, outside the resource folder. Keep a copy of your config folder before replacing the resource with a new version.

Water Monitor / Deck Gun Script

Fire Apparatus Deck Gun · v1.0.0

SWR Water Monitor puts a working deck-gun style water monitor on your fire trucks. Firefighters aim it with an on-screen joystick from the passenger seat or standing next to the truck, set the flow, and can leave it flowing on the fire. The stream follows a real arc and puts out fires where it lands, and every player sees the same thing. It runs standalone on any server: no framework, no database. Resource name: cfx-swr-water-monitor.

Features

The monitor

  • Custom 3-part model (base, swivel, barrel) that turns and raises on the truck.
  • 270° of turn (±135°) and −30° to +80° elevation, with limits you can set per truck.
  • Motor-driven movement at a steady speed, not instant snapping.

The water

  • The jet comes out of the nozzle tip and follows the barrel as it aims.
  • Flow from 0 to 100%. More flow gives a longer reach and a bigger stream.
  • The stream is traced as an arc, and GTA fires where it lands are put out.
  • An optional water push at the impact shoves peds and loose objects.
  • Aim, flow and on/off are stored on the truck. The monitor keeps flowing with nobody in it, and everyone nearby sees the same thing.

The controller

  • A joystick with arrow buttons, live turn and elevation gauges, a flow slider, and water on/off and center buttons.
  • Opens from the passenger seat ("Press E" prompt or /watermonitor) or on foot next to the truck (/monitorcontroller).

For admins

  • /positionnozzle places the monitor on any truck in game and prints the config line. It detects addon spawn names and the mount bone.
  • One line of config per truck.

Requirements

  • Required: a FiveM server on a recent artifact with OneSync enabled.
No framework. No database. ESX, QBCore, SQL and other resources are not needed.

Installation

  1. 1Download the resource from your Cfx.re Keymaster.
  2. 2Put the cfx-swr-water-monitor folder in your server's resources folder. Keep the folder name as it is.
  3. 3Add it to server.cfg, along with permission for staff to use the placement tool:
    ensure cfx-swr-water-monitor
    add_ace group.admin command.positionnozzle allow
  4. 4Restart the server, then add the monitor to your trucks.

No trucks are set up out of the box. Every server's fleet is different, so you place the monitor on your own trucks. It takes about a minute per truck.

Adding the Monitor to Your Trucks

  1. 1Spawn the truck, then get in it or stand next to it.
  2. 2Run /positionnozzle. A see-through, outlined monitor appears on the truck.
  3. 3Check the top-left of the screen. Spawn nameis detected automatically: base-game trucks are recognised straight away, and for addon trucks the server looks the name up in your resources' vehicles.metafiles, so it may show "detecting..." for a moment. Bone is the part of the truck the monitor is attached to. It starts on chassis (or bodyshell if the truck has no chassis bone), and the screen also shows the nearest bone to the monitor.
  4. 4Move the monitor into place with the keys below. The monitor's "straight ahead" is the direction the barrel points in the preview. Point it toward the front of the truck unless you want a different rest position.
  5. 5Press Enter. A config line is printed to the F8 console (example below).
  6. 6Paste it into Config.Vehicles in config.lua and restart the resource.
KeyAction
Arrow keysMove left / right / forward / back
Page Up / Page DownMove up / down
Q / ERotate
RChoose what Q / E rotates: heading, pitch or roll
B / Shift+BNext / previous bone, nearest first
Shift (hold)Fine adjustment
EnterSave
Backspace / EscCancel
['firetruk'] = { bone = 'chassis', offset = vec3(0.000, 1.200, 1.950), rotation = vec3(0.0, 0.0, 0.0) },

To adjust a truck later, run /positionnozzle on it again. The tool starts from its current position.

About bones: leave the monitor on chassis or bodyshell for a fixed mount. Only switch to a moving part, such as a ladder bone, if the monitor should move with it. Switch bones while that part is stowed.

If the spawn name isn't found: run /positionnozzle <spawnname>. You can also start on a specific bone with /positionnozzle <spawnname> <bone>.

Firefighter Guide

Opening the controller

  • From the passenger seat: sit in the front passenger seat of a truck with a monitor. For the first 5 seconds, a prompt says Press E to open the water monitor controller. After that, use /watermonitor.
  • On foot: stand within 3 m of the truck and use /monitorcontroller.

Which seats can operate the monitor is set by the server owner, and is the front passenger seat by default.

Using the controller

  • Joystick: drag the red knob for proportional speed (a small push turns it slowly, a full push at motor speed), or press and hold an arrow. Up raises the barrel; left and right swing it.
  • Gauges: the left dial is a top-down view of the truck showing which way the monitor points. The right dial shows elevation. The blue arc on each is the allowed range.
  • Flow: the slider and −/+ buttons set flow from 0 to 100%. More flow reaches further.
  • Water: turns the stream on or off. The dot next to the title glows blue while it's flowing.
  • Center: brings the monitor back to straight ahead and level.
Key (while the controller is open)Action
WASD / arrow keysAim
SpaceWater on / off
+ / −Flow up / down
CCenter
Esc / BackspaceClose

The monitor turns at motor speed, so the gauges show it catching up to where you're aiming.

Leaving it flowing

Close the controller, get out or walk away: the monitor stays where it was aimed, and the water keeps flowing until someone switches it off. On foot, control ends when you close the controller or walk more than a few metres from the truck.

Configuration Reference

Everything is in config.lua. The settings you're most likely to change:

SettingDefaultWhat it does
Config.VehiclesemptyTrucks with a monitor. Generate entries with /positionnozzle.
Config.ControlSeats{ 0 }Seats that can operate the monitor (-1 driver, 0 front passenger, 1+ rear).
Config.Controller.promptTime5000How long (ms) the "Press E" prompt shows after sitting down.
Config.Controller.command'watermonitor'Command to open the controller from a seat. false to disable.
Config.Controller.proximity.command'monitorcontroller'Command to open it on foot. false to disable.
Config.Controller.proximity.distance3.0How close (m) to the truck's body you must be on foot.
Config.Controller.side'right'Screen side for the controller.
Config.Limits±135° turn, −30° to +80° elevationDefault aim limits.
Config.MotorSpeed30°/s turn, 20°/s elevationHow fast the monitor moves.
Config.Flow.default50Flow of a truck nobody has touched yet.
Config.Stream.speedMin / speedMax12 / 28 m/sWater speed at lowest / full flow. Sets the reach.
Config.Extinguish.radiusMin / radiusMax1.5 / 4.0 mFire-out radius at lowest / full flow.
Config.Extinguish.pushEntitiestrueWater push at the impact that shoves peds and objects.
Config.StreamDistance150.0Monitors are shown on trucks within this distance.
Config.Keyboard.enabledfalseRebindable keys to operate from a seat without opening the controller.
Config.PositionTool.restrictedtrue/positionnozzle needs the command.positionnozzle ace.
Config.DebugfalseDraws the stream's arc and the nozzle tip, for tuning.

Per-truck options. Any Config.Vehicles entry can override the limits and seats:

['firetruk'] = {
    bone = 'chassis', offset = vec3(0.000, 1.200, 1.950), rotation = vec3(0.0, 0.0, 0.0),
    limits = { yawMin = -90, yawMax = 90 },  -- e.g. a monitor that can't swing over the cab
    seats = { 0, 1 },                        -- front passenger and rear left
},

Effects. Config.Particlesets the jet and impact effects and their size. If the jet doesn't leave along the barrel on your build, adjust Config.Particle.rotation. Turn on Config.Debug to compare the jet with the traced arc.

Integrations & Exports

Fire scripts.The monitor puts out GTA fires where the stream lands. Fire scripts that manage their own fires can listen for the impact. It fires on one client per truck (the truck's network owner), every 250 ms while the stream is hitting something:

AddEventHandler('cfx-swr-water-monitor:waterImpact', function(coords, radius, flow, vehicle)
    -- coords: vector3 where the stream lands, radius: metres, flow: 0-100
end)

Client exports. These act on the truck the player is currently operating (from a seat or on foot):

ExportDoes
GetControlledMonitor()The truck being operated, or nil
GetMonitorState(){ yaw, pitch, flow, on }
SetMonitorAim(yaw, pitch)Aim in degrees (positive yaw = left)
NudgeMonitor(dYaw, dPitch)Aim relative to the current position
SetMonitorFlow(flow)Flow 0–100
SetMonitorWater(on)Water on / off
ToggleMonitorWater()Switch water
exports['cfx-swr-water-monitor']:SetMonitorFlow(80)

Client event cfx-swr-water-monitor:controlChanged (vehicle | nil, state, onFoot) fires when the player starts or stops operating a monitor.

State bag.Each truck's monitor state is in the vehicle state bag swrMonitor ({ yaw, pitch, flow, on }), readable from client or server:

local state = Entity(vehicle).state.swrMonitor

Commands

CommandWhoDescription
/watermonitorAnyone in a control seatOpens the controller
/monitorcontrollerAnyone on foot near a truckOpens that truck's controller
/positionnozzle [spawnname] [bone]Staff (command.positionnozzle ace)Places the monitor on a truck and prints its config line
/watermonitordebugAnyonePrints diagnostics to F8 (see troubleshooting)

FAQ & Troubleshooting

IssueFix
The monitor doesn't appear on my truckStand next to it and run /watermonitordebug, then check F8. config=NO MATCH means the spawn name in Config.Vehicles isn't this truck's: re-run /positionnozzle on it. registered=false on a model means the resource's stream folder is missing or incomplete: re-download the resource.
The monitor is in the wrong placeIf /watermonitordebug shows the bone with index -1, that bone doesn't exist on this truck and the monitor falls back to the truck's centre. Re-run /positionnozzle on it, or use chassis.
/positionnozzle says "Access denied" or does nothingAdd add_ace group.admin command.positionnozzle allow to server.cfg and make sure you're in that group.
The spawn name shows "unknown"The truck's vehicles.meta couldn't be found or read. Run /positionnozzle <spawnname> with the name you spawn it with.
The water doesn't put out my fire script's firesThe monitor puts out GTA fires directly. Fires managed by another script need that script to listen for the waterImpact event, or ask its author to.
The water reaches further or shorter than the stream looksTurn on Config.Debug to see the traced arc, then tune Config.Stream (reach) or Config.Particle (look) until they match.
Can the driver operate it?Yes: add -1 to Config.ControlSeats, or to the truck's own seats.
The water push knocks people overSet Config.Extinguish.pushEntities = false.

Variable Siren

Mechanical Q Siren · v0.1.0

SWR Variable Siren gives your fire apparatus a mechanical siren based on the classic Q. Hold the pedal and the rotor winds up; let go and it coasts down through the long, low growl, just like the foot switch on a real truck. There are no audio files: every tone is generated live on each player's PC from the rotor's speed, so the pitch is truly continuous and nothing has to be downloaded. It runs standalone on any server: no framework, no database. Resource name: cfx-swr-var-siren.

Features

No audio files

  • Nothing is sampled or played back. No .ogg, .mp3 or .awc files, no sound packs, no game-file replacement. The whole resource is about 40 KB.
  • The tone is synthesized and modulated live on each player's machine, using the browser audio engine built into FiveM's NUI.
  • Only the pedal and brake switch states go over the network. Players have nothing to download when they join.

A siren that behaves like a rotor

  • Hold the pedal and the rotor spins up (about 4 seconds to 95% speed).
  • Release it and the rotor coasts down. Defaults are fitted to a recording of a real Q mechanical siren (full speed to 40% in about 13 seconds).
  • Hold the brake to stop it short.
  • Feather the pedal to hold any pitch.

Real 3D sound

  • The sound comes from the truck's front bumper and projects forward. It's quieter behind the truck.
  • HRTF 3D panning, Doppler shift as the truck passes, and a smooth fade at the edge of range.
  • Muffled when you're inside a cab in first person.

Synced for everyone

  • Every nearby player hears the same rotor speed.
  • The driver and the front passenger both have switches, which work in parallel. The passenger switch can be turned off.
  • The server checks every switch change, so only someone seated in an allowed truck can work it.

Requirements

  • Required: a FiveM server on a recent artifact with OneSync enabled.
No framework. No database. ESX, QBCore, SQL and other resources are not needed.

Installation

  1. 1Download the resource from your Cfx.re Keymaster.
  2. 2Put the cfx-swr-var-siren folder in your server's resources folder. Keep the folder name as it is.
  3. 3Add it to server.cfg:
    ensure cfx-swr-var-siren
  4. 4Restart the server. Get in any emergency vehicle and hold J.

Out of the box the siren works on every emergency-class vehicle. To limit it to your fire apparatus, see Choosing Which Vehicles Get the Siren.

Using LVC or another siren script?Turn off its Q / mechanical tone on the trucks that use this siren, so the two don't play over each other. Both scripts can run side by side.

Choosing Which Vehicles Get the Siren

Open config.lua and list spawn names in Config.Vehicles:

Config.Vehicles = {
    'firetruk',
    'my_addon_engine',
    'my_addon_ladder',
}
  • Empty list: every emergency-class vehicle (class 18) gets the siren, police cars and ambulances included.
  • Any names listed: only those models get it. Addon vehicles work the same as base-game ones: use the name you spawn them with.

Restart the resource after changing the list.

Operator Guide

Controls

Key (default)Action
J (hold)Pedal: the motor drives the rotor up toward full speed
K (hold)Brake: stops the rotor short instead of letting it coast

Each player can rebind these under Settings > Key Bindings > FiveM. Look for "Variable Siren - Pedal (hold)" and "Variable Siren - Brake (hold)".

You can operate the siren from the driver's seat, and from the front passenger seat unless the server has turned that off. If both of you press the pedal, it acts as one switch, just like on a real truck.

Working the pedal

  • Wind it up: hold the pedal. The pitch climbs quickly at first, then levels off at full speed.
  • Let it growl: release the pedal. The rotor drops fast at the top, then runs down slowly through the low growl.
  • Hold a pitch: tap the pedal on and off to keep the rotor where you want it.
  • Cut it off: hold the brake to stop the rotor quickly. Braking a stopped rotor does nothing.

If you get out of the truck with the pedal held, your switch is released and the rotor coasts down by itself.

Volume

Each player sets their own siren volume:

/varsirenvolume 60

Use any value from 0 to 100. The setting is saved on the player's PC. Run /varsirenvolume with no number to see the current value. The siren is silent while the pause menu is open.

How the Sound Is Made

Nothing in this resource is a recording. Each client builds the siren from scratch, many times a second:

  1. 1Rotor physics. The rotor's speed (0 = stopped, 1 = full speed) is worked out every frame from the pedal and brake. The motor pulls it up; bearing friction and air drag slow it down.
  2. 2Pitch. Pitch is rotor speed × MaxFreq (911 Hz at full speed by default). It changes smoothly, with no loops or crossfades.
  3. 3Tone. Two slightly detuned oscillators play a 24-harmonic "chopped air" wave, then pass through filters that copy the resonances of the siren housing. As the pitch sweeps, different harmonics pass through those resonances, which gives the siren its changing voice.
  4. 4Character. Rotor-imbalance growl at shaft speed, air rush that follows the pitch, motor whine, and soft clipping for grit.
  5. 5Placement. The result is placed at the truck's front bumper in 3D, with a forward cone, Doppler shift and distance falloff, relative to the player's camera.

The server only syncs which switches are held. Every client runs steps 1–5 itself, so the operator hears their own truck respond instantly.

Tuning the Sound

You can retune the siren without touching any audio files.

The preview bench

Open html/index.html from the resource folder in any web browser(Chrome, Edge, Firefox). You don't need the game running.

  • Hold PEDAL (or Space) and BRAKE (or Shift) to run the rotor.
  • Drag the sliders to change the sound and the physics. The meter shows the pitch and rotor speed.
  • Change Bench.distance to hear it from further away, and tick the muffle box to hear it from inside a cab.
  • Copy the Config snippet at the bottom into config.lua, replacing the matching values.
The snippet covers the slider values only. Harmonics and Body aren't on the bench; edit those in config.lua by hand. The bench starts from the default settings, not from your config.lua. If you've already changed values, set the sliders to match before you start.

What to change

To make it…Change
Higher or lower overallConfig.Sound.MaxFreq
Wind up faster or slowerConfig.Physics.SpinUp
Coast down longer or shorterConfig.Physics.Aero (top end), Config.Physics.Friction (low growl)
Stop faster with the brakeConfig.Physics.Brake
Growl more at low speedConfig.Sound.Wobble
Sound rougher or cleanerConfig.Sound.Drive
Louder or quieter for everyoneConfig.Sound.Gain
Carry furtherConfig.RefDistance, Config.MaxDistance

Configuration Reference

Everything is in config.lua, which stays open and editable after escrow.

General

SettingDefaultWhat it does
Config.Keys.pedal'J'Default pedal key. Players can rebind it.
Config.Keys.brake'K'Default brake key. Players can rebind it.
Config.VehiclesemptyModels that have the siren. Empty = every emergency-class vehicle.
Config.AllowPassengertrueLets the front passenger work the siren too.
Config.DefaultVolume80Starting volume (0–100) for players who haven't set one.
Config.VolumeCommand'varsirenvolume'Name of the personal volume command.

FiveM saves each player's key binding the first time they join. Changing Config.Keyslater only affects players who haven't joined yet; others keep their binding until they change it in Settings.

Rotor physics — Config.Physics

Rotor speed is 0.0 (stopped) to 1.0 (full speed).

SettingDefaultWhat it does
SpinUp0.75How fast the motor winds it up. Roughly 3 / SpinUp seconds to 95%.
Friction0.006Constant bearing drag. Mostly sets how long the low growl lasts.
Viscous0.0Drag that grows with speed.
Aero0.099Air drag, strongest at full speed. Sets how fast it falls from the top.
Brake0.55Extra slowing while the brake is held.

Sound — Config.Sound

SettingDefaultWhat it does
MaxFreq911.0Pitch (Hz) at full speed.
Ports7Rotor ports. Sets the shaft speed used for growl and motor whine.
Gain0.9Overall loudness at full speed, before distance.
LoudCurve0.0Extra quietening at low speed. 0 leaves it to the housing filters.
Detune4Cents between the two outputs (slow beating).
Noise0.12Air rush.
Wobble0.16Rotor-imbalance growl.
Hum0.03Motor whine.
Drive0.6Grit (soft clipping). High values get harsh.
Panning'HRTF''HRTF' for better 3D, or 'equalpower' for a cheaper stereo pan.
Harmonics24 valuesLevel of each harmonic in dB, relative to the fundamental.
Body7 filtersThe housing's fixed resonances. Each is highpass, lowpass or peaking.

3D audio

SettingDefaultWhat it does
Config.RefDistance12.0Distance (m) at which the siren plays at full Gain.
Config.Rolloff1.0How quickly it gets quieter with distance.
Config.MaxDistance450.0Beyond this (m) the siren isn't played at all.
Config.FadeDistance60.0Fades to silence over the last N metres before MaxDistance.
Config.Doppler1.0Doppler pitch shift. 0 = off, 1 = physically correct.
Config.SourceHeight0.9Height (m) of the sound above the bottom of the vehicle, at the front bumper.
Config.Cone.inner / outer140 / 300Angles (°) of the forward projection.
Config.Cone.outerGain0.45Volume behind the truck, outside the cone.

Cab muffle — Config.Muffle

SettingDefaultWhat it does
enabledtrueMuffle the siren when the listener is in a vehicle in first person.
cutoff1400.0Low-pass frequency (Hz) while muffled.
gain0.7Volume while muffled.

Advanced

SettingDefaultWhat it does
Config.UpdateRate60Times per second the sound engine is updated.
Config.StateBagKey'swrVarSiren'State bag key on the vehicle (see Integrations).

Integrations

State bag. Who is holding the pedal or brake on each truck is stored in the vehicle state bag swrVarSiren, which you can read from client or server. Its value is a table of server ID → switch bits (1 = pedal, 2 = brake), one entry per operator:

local ops = Entity(vehicle).state.swrVarSiren or {}
local running = false
for serverId, bits in pairs(ops) do
    if bits & 1 ~= 0 then running = true end
end

Only the server writes this bag. The rotor speed itself isn't networked: each client works it out from the switch states.

Commands & Keybinds

Command / keyWhoWhat
J (hold)Driver or front passengerPedal: spin the rotor up
K (hold)Driver or front passengerBrake: stop the rotor short
/varsirenvolume [0-100]AnyoneSets or shows your own siren volume

FAQ & Troubleshooting

IssueFix
Do I need to install any sound files?No. There are none. The siren is generated live on each player's PC.
Nothing happens when I hold JMake sure you're in the driver's seat (or the front passenger seat, if Config.AllowPassenger is on). If Config.Vehicles has names in it, this vehicle's spawn name must be one of them; if it's empty, the vehicle must be emergency class. Check your binding under Settings > Key Bindings > FiveM (another script may use J), check your volume with /varsirenvolume, and make sure OneSync is enabled on the server.
I hear two sirens at onceAnother siren script (such as LVC) is playing its own Q tone on the same truck. Turn that tone off for these trucks.
Police cars have the siren tooWith an empty Config.Vehicles, every emergency-class vehicle gets it. List your fire apparatus models to limit it.
The passenger can't work the sirenSet Config.AllowPassenger = true.
I changed Config.Keys but my key didn't changeFiveM keeps each player's saved binding. Change it under Settings > Key Bindings > FiveM.
It's too quiet from down the streetRaise Config.RefDistance (full volume further out) or Config.Sound.Gain. To hear it further away at all, raise Config.MaxDistance.
It sounds muffledThat's the cab effect when you're inside a vehicle in first person. Set Config.Muffle.enabled = false to turn it off, or raise Config.Muffle.cutoff.
The siren sounds harsh or distortedLower Config.Sound.Drive, and check Config.Sound.Gain isn't above 1.
Can the siren have other tones, like wail or yelp?No. This resource is a single mechanical (Q-based) siren. Use your existing siren script for electronic tones.

AI Towing V2

Coming Soon

AI Towing V2

AI Towing V2 is a complete rebuild of our original AI Towing script. V2 features smarter NPC dispatch logic, multi-vehicle tow support, fully configurable tow zones and cooldowns, and native integration with popular FiveM frameworks like ESX and QBCore.

In Development

Full documentation, configuration reference, and installation guide will be published here when AI Towing V2 releases. Join our Discord to be notified first.

Can't find what you need? Ask in our community.

Join Discord