Installation
Requirements
- A recent FiveM server artifact (uses entity state bags and Lua 5.4)
Nothing else is required. The NUI in html/ is plain HTML/CSS/JS with vendored fonts, so there is nothing to build.
Optional dependencies
Everything below is auto-detected. Start these resources before np_helicam.
| Kind | Resources | What it adds |
|---|---|---|
| Framework | qbx_core, qb-core, es_extended | Jobs and duty for the air / ground / bolo roles, and callsigns (qb / qbx metadata.callsign) |
| Fuel | ox_fuel, LegacyFuel, cdn-fuel, ps-fuel | FUEL % and endurance on the HUD. Without one, the native fuel level is used, and nothing is shown if it is 0 |
| Notifications | np_hud, ox_lib | Used when Config.Notify.provider is 'np_hud' or 'ox_lib'. Otherwise the built-in Nimbus toasts |
| Evidence | screenshot-basic | B (bookmark) also takes an evidence screenshot |
Steps
- Put the resource in
resources/asnp_helicam. The NUI callbacks use the resource name. - Add it to
server.cfg, after its optional dependencies:cfgensure qbx_core # or qb-core / es_extended, if you use one ensure ox_fuel # optional ensure np_hud # optional, for Config.Notify.provider = 'np_hud' ensure screenshot-basic # optional, evidence screenshots ensure np_helicam - Open
config.luaand set at least:Config.Helicopters: the models that have the cameraConfig.Jobs: which jobs get theair,groundandboloroles (framework servers)Config.Callsigns: the air callsign, per model if you likeConfig.DepartmentandConfig.Callsign: the text in the HUD header
- Optional: set the Discord webhook for mission reports (see below).
Start order
The framework bridge on the server re-detects when qbx_core, qb-core or es_extended starts after np_helicam. Fuel and notification providers are looked up when they are used. The safe order is still: dependencies first.
Roles
There are three roles. The server checks them on every action, and the client only uses its copy to show or hide UI.
| Role | Grants |
|---|---|
air | Open the camera, place / rename / delete markers, BOLO checks from ANPR, mission recording, crew share |
ground | The F7 Air Support panel, the live feed, orbit requests, BOLO and share alerts |
bolo | Manage the BOLO list with /bolo (the role is set by Config.Bolo.commandJobs) |
air and ground receive the marker list and can subscribe to the friendly-unit positions. Either role, or sitting in a camera heli, opens the F9 settings sheet. bolo is separate so that, for example, EMS keep the Air Support panel but can't flag plates.
With a framework
Config.Framework = 'auto' picks the first of qbx_core, qb-core, es_extended that is started. A player has a role when their job name is in Config.Jobs.air / .ground / .bolo:
Config.Jobs = {
air = { 'police', 'sheriff', 'lspd', 'bcso', 'sasp' },
ground = { 'police', 'sheriff', 'lspd', 'bcso', 'sasp', 'ambulance' },
bolo = { 'police', 'sheriff', 'lspd', 'bcso', 'sasp' },
requireDuty = true, -- qb / qbx: job.onduty must be true
standaloneAllow = true,
}ESX has no duty flag, so having the job counts as on duty. Job and duty changes are picked up through the framework events (QBCore:Server:OnJobUpdate, QBCore:Server:SetDuty, esx:setJob, …) and pushed to the client.
Standalone (ACE)
Without a framework, Config.Jobs.standaloneAllow = true gives everyone every role, like v1. To restrict it, set it to false and grant ACE permissions:
add_ace group.police np_helicam.air allow
add_ace group.police np_helicam.ground allow
add_ace group.police np_helicam.bolo allowWith standaloneAllow = false, only players with the ACE have the role. ACE is only consulted in standalone mode.
Config.CanUse
Config.CanUse(ped, heli) still exists. It runs on the client each time the camera opens and each time the searchlight or hover key is pressed outside the camera. Return false to deny:
Config.CanUse = function(ped, heli)
return GetVehicleLivery(heli) == 0 -- e.g. only the police livery
endClient-side only
Config.CanUse is a UX gate, not a security boundary. The real gate is the role check on the server. Every server handler re-checks the role, the seat (for air actions) and rate limits. Clients write no state bags: the server validates what the operator sends and writes the bags itself.
Discord webhook
Finished mission reports can be posted to a Discord channel. The URL is a server convar, never part of config.lua, because that file is shared and is sent to every client:
set np_helicam_webhook "https://discord.com/api/webhooks/..."It must start with https://. The post is an embed with the plain-text report, the duration, distance, event count and operator. Screenshots are not uploaded to Discord. See Operations → Mission recording.
Troubleshooting
| Symptom | Fix |
|---|---|
E does nothing | Check that the model is in Config.Helicopters (the lowercase spawn name, matched by model hash), that you sit in a seat in Config.CameraSeats, that you have the air role (job + duty, or ACE), and that Config.CanUse returns true. In the first second after joining, the role may not have arrived yet. |
E, F7 or F9 conflicts with another script | Rebind it in Settings → Key Bindings → FiveM. Every action is a key mapping. The settings sheet uses F9 because qb-radialmenu binds F1. |
F7 does nothing | You don't have the ground role. Check Config.Jobs.ground, duty, or the np_helicam.ground ACE. Without the role, F7 is silent on purpose. |
F9 does nothing | You have neither the air nor the ground role and don't sit in a camera heli. |
/bolo says "Not authorised." | You don't have the role in Config.Bolo.commandJobs (default bolo: Config.Jobs.bolo, or the np_helicam.bolo ACE). |
| Minimap is in the wrong place after closing the camera or the Air Support panel | Another HUD (e.g. np_hud) placed the minimap. Set Config.ContextMap.restore to its layout, or have it listen for np_helicam:contextMap. |
| No FLIR right after opening the camera | FLIR warms up for Config.Thermal.warmupMs (8 s). The FLIR pill shows the percentage. |
| Grainy picture | Config.Camera.cctvEffect is on (server-wide), or the player turned on CCTV grain in F9. |
| No fuel on the HUD | No fuel resource is started and the native fuel level is 0. |
| Hover won't engage | You are below Config.Hover.minAltitude (15 m) or faster than Config.Hover.maxEngageSpeed (12 m/s). Hover is a hold, not an autobrake. |
B takes no screenshot | screenshot-basic is not started, or Config.Mission.screenshots is false. One screenshot per 5 s per player. |
| A searchlight stays lit after the operator left | The server sweep turns it off within about 10 s once its operator is no longer in a camera seat with the air role. See Searchlight sync. |