diff --git a/pages/radio/_meta.ts b/pages/radio/_meta.ts index 08042a2..dc3fd7a 100644 --- a/pages/radio/_meta.ts +++ b/pages/radio/_meta.ts @@ -1,6 +1,7 @@ export default { description: "Description", - installationguide: "Installation guide", - faq: "Frequently asked questions", - devdocs: "Developer documentation" + installationguide: "Installation Guide", + configurationguide: "Configuration Guide", + faq: "Frequently Asked Questions", + devdocs: "Developer Documentation" }; diff --git a/pages/radio/configurationguide.mdx b/pages/radio/configurationguide.mdx new file mode 100644 index 0000000..7589416 --- /dev/null +++ b/pages/radio/configurationguide.mdx @@ -0,0 +1,921 @@ +import { Callout } from "nextra/components"; + +# Configuration Guide + +All of zerio-radio's settings are Lua files in the `configs/` folder. This guide explains every option and its default. Restart zerio-radio after you change a config file. + +Channels, towers, signal zones, speakers and patches are not in the config files. They are stored in the database and managed in the in-game admin menu (`/radioadmin`). + +## Configuration Files Overview + +| File | Purpose | +| ---------------------------------- | -------------------------------------------------------- | +| `configs/main.lua` | Language, general radio behaviour, commands, callsigns | +| `configs/voice.lua` | Connection to the hosted voice server | +| `configs/themes.lua` | UI color themes players can choose from | +| `configs/gameplay.lua` | Channel limit, disconnect rules, shooting restriction | +| `configs/framework.lua` | Framework detection (don't edit) | +| `configs/admin.lua` | Admin menu command and standalone ace | +| `configs/opentype.lua` | How players open the radio, radio items, codeplug radios | +| `configs/audio/ambient.lua` | Gunshots and aircraft in transmissions | +| `configs/audio/micclicks.lua` | Push-to-talk sounds | +| `configs/audio/submix.lua` | How audio quality drops with weak signal | +| `configs/features/towers.lua` | Tower coverage, destructible towers, tower repair | +| `configs/features/panic.lua` | Panic button | +| `configs/features/dispatch.lua` | Panic and man-down alerts for dispatch scripts | +| `configs/features/scanner.lua` | Radio scanner | +| `configs/features/animation.lua` | Radio props and animations | +| `configs/features/audio-leak.lua` | Nearby players hearing your radio, earpieces | +| `configs/features/jammers.lua` | Ground and personal jammers | +| `configs/features/repeaters.lua` | Repeaters | +| `configs/features/cutoffzones.lua` | Signal zones | +| `configs/features/occlusion.lua` | Signal loss underground | +| `configs/features/weather.lua` | Signal loss in bad weather | +| `configs/features/battery.lua` | Battery drain, swaps and charging stations | +| `configs/features/patches.lua` | Channel patches | +| `configs/features/mandown.lua` | Man-down alerts | +| `configs/features/gps.lua` | Unit GPS blips | +| `configs/features/speakers.lua` | PA speakers | + +--- + +## Main Settings (`configs/main.lua`) + +### Language + +```lua +Config.Locale = "en" +``` + +The language for notifications and command help. It must match a file in `locales/`. zerio-radio ships with `en` (English) and `sv` (Swedish). See [Translations](#translations-locales). + +### Radio Behaviour + +| Option | Default | Description | +| ---------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Config.RemoveWeaponOnRadio` | `true` | Holsters the player's weapon when they open the radio and while they hold push-to-talk, and gives it back afterwards. With ox_inventory, ox_inventory's own disarm is used. | +| `Config.DisableAnonymous` | `false` | When `true`, players can't hide their name on the radio. | +| `Config.ShowPlayerList` | `true` | Shows the member list overlay while the player is in a channel. | +| `Config.DisplayRadioName` | `false` | Shows the channel name instead of "Radio members" above the member list. | +| `Config.MessageLimit` | `50` | How many messages to load when a player opens a conversation. `-1` loads all. | +| `Config.RecentLimit` | `10` | How many recently used channels to keep per player. | + +### Commands + +```lua +Config.ChangeRadioChannelCommand = "changeradio" +``` + +The command that joins a frequency without opening the radio, for example `/changeradio 911`. Set it to `nil` or remove the line to remove the command. + +### Callsigns + +```lua +Config.AllowCallSigns = true +Config.CallsignCommand = "callsign" +``` + +- `AllowCallSigns`: Lets players set a callsign that shows next to their name. +- `CallsignCommand`: The command to set a callsign, for example `/callsign 1-ADAM-12`. Set it to `nil` to remove the command. Players can still set a callsign in the radio. + +### Defaults for New Players + +```lua +Config.DefaultTheme = "orange" +Config.DefaultSkin = "default" +``` + +- `DefaultTheme`: The theme new players start with. Must match an `id` in `configs/themes.lua`. If you remove it, the first theme in the list is used. +- `DefaultSkin`: The radio skin new players start with: `"default"`, `"sharp"` or `"rounded"`. + +Players can change both in the radio's settings. + +--- + +## Voice Server (`configs/voice.lua`) + +```lua +Config.VoiceServer = { + Enabled = true, + Domain = "eu.radio.zerio-scripts.com", + ServerId = "my-rp-server", + PortalUrl = "https://portal.radio.zerio-scripts.com", +} +``` + +| Option | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Enabled` | Turns the voice connection on. When `false`, there is no radio audio and no dispatch panel connection. | +| `Domain` | The voice region host from the setup wizard: `eu.radio.zerio-scripts.com` or `na.radio.zerio-scripts.com`. Only the host, with no `https://` and no trailing slash. | +| `ServerId` | The Server ID you entered in the setup wizard. | +| `PortalUrl` | The customer portal. zerio-radio uses it to sync patches made in the web dispatch panel. Set it to `"https://portal.radio.zerio-scripts.com"`, or `""` to turn the sync off. | + + + The file ships with local development values (`Domain = "localhost:30130"`, + `ServerId = ""`, `PortalUrl = "http://localhost:3000"`). Replace all three + with your own values, or the radio can't connect. + + +The admin token is not in this file. It goes in `server.cfg`, with `set` and never `setr`: + +```cfg +set zerio_voice_admin_token "your-token-from-the-setup-wizard" +``` + +See [Set Up the Voice Server](/radio/installationguide#set-up-the-voice-server) for where to get these values. + +--- + +## Themes (`configs/themes.lua`) + +`Config.Themes` is the list of color themes players can pick in the radio's settings. zerio-radio ships with eight: `orange`, `midnight`, `emerald`, `slate`, `mono`, `gold`, `rose` and `teal`. Remove the ones you don't want, or add your own. + +Colors are HSL values without the `hsl()` wrapper, for example `"271 81% 56%"`. Only `id`, `label` and `primary` are required. Anything you leave out uses the built-in default. Here is a list with one custom theme; to keep the shipped themes, add your entry to the existing list instead: + +```lua +Config.Themes = { + { + id = "purple", + label = "Purple", + primary = "271 81% 56%", + primaryDark = "271 85% 64%", + backgroundDark = "271 20% 7%", + cardDark = "271 16% 11%", + }, +} +``` + +| Field | Description | +| ------------------------------- | ----------------------------------------------------------- | +| `id` | Unique ID. `Config.DefaultTheme` refers to this. | +| `label` | Name shown to players. | +| `primary` / `primaryDark` | Accent color in light mode / dark mode. | +| `primaryFg` | Text color on accent-colored buttons. Default `"0 0% 98%"`. | +| `background` / `backgroundDark` | Page background in light / dark mode. | +| `card` / `cardDark` | Card and panel background. | +| `border` / `borderDark` | Border color. | +| `muted` / `mutedDark` | Secondary surfaces such as badges and inputs. | +| `mutedFg` / `mutedFgDark` | Dimmed text. | +| `input` / `inputDark` | Input field background. | +| `foreground` / `foregroundDark` | Main text color. | + +--- + +## Gameplay (`configs/gameplay.lua`) + +| Option | Default | Description | +| ------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Config.MaxChannels` | `3` | How many channels a player can be on at once, counting the active channel and listen-only channels. `-1` removes the limit. | +| `Config.DisconnectOnDeath` | `false` | When `true`, players leave all channels when they die. Dead players can never transmit, but by default they stay on their channels so man-down alerts still work. | +| `Config.DisconnectInWater` | `false` | When `true`, players leave all channels and can't open the radio while in water. | +| `Config.MinimumWaterDepth` | `-1` | Used with `DisconnectInWater`. `-1` means any water counts. | +| `Config.RemoveOnOffDuty` | `true` | QBCore and Qbox only. Removes players from channels when they go off duty. | +| `Config.DisableShootingWhileTalkingOnRadio` | `false` | Blocks shooting while the player holds push-to-talk. | + + + `RemoveOnOffDuty` removes the player from every channel created in + `/radioadmin` when they go off duty, not only from job-restricted channels. + + +--- + +## Framework (`configs/framework.lua`) + +This file detects your framework when the resource starts. You don't need to edit it. + +- `qb-core` or `qbx_core` running: QBCore mode (Qbox runs through its QBCore compatibility). +- `es_extended` running: ESX mode. +- Neither: standalone mode. + +--- + +## Admin Menu (`configs/admin.lua`) + +```lua +Config.Admin = { + command = "radioadmin", + acePermission = "zerio.admin", +} +``` + +- `command`: The command that opens the admin menu. +- `acePermission`: The ace that gives admin access on standalone servers. + +Who counts as an admin depends on the framework: + +| Framework | Who has access | +| --------------- | --------------------------------------------------------------------------------------- | +| QBCore and Qbox | Players with the `admin` permission (`QBCore.Functions.HasPermission(source, "admin")`) | +| ESX | Players in the `admin` or `god` group | +| Standalone | Players with the ace in `acePermission` | + +For standalone, add the ace in `server.cfg`: + +```cfg +add_ace group.admin zerio.admin allow +``` + +Players who are not admins but have patch access (see [Channel Patches](#channel-patches-configsfeaturespatcheslua)) can also open the admin menu. They only see the Patches page. + +--- + +## Opening the Radio (`configs/opentype.lua`) + +### Open Types + +```lua +Config.OpenType.Value = { "item" } +Config.OpenType.Keybind = "f5" +Config.OpenType.Command = "radio" +``` + +`Config.OpenType.Value` is a list of ways players can open the radio: + +| Value | How the radio opens | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| `"item"` | By using a radio item from `Config.OpenType.Items`. The player must carry and use the item. | +| `"command"` | With the command in `Config.OpenType.Command` (`/radio`). | +| `"keybind"` | With a keybind. The default key is `Config.OpenType.Keybind` (F5); players can rebind it. | +| `"custom"` | Only from your own scripts, through the `zerio-radio:client:open` client event or the `Open` export. See [Developer Documentation](/radio/devdocs). | + +`"command"`, `"keybind"` and `"custom"` can be combined, for example `{ "command", "keybind" }`. `"item"` can't be combined with anything: if you list `"item"` with other types, the others are ignored and a warning is printed. That keeps players from getting around the item requirement. + + + Standalone servers have no inventory. Change `Config.OpenType.Value` to + `"command"` and/or `"keybind"`, or nobody can open the radio. + + +### Radio Items and Codeplug Radios + +`Config.OpenType.Items` lists every item that works as a radio. It is only used in item mode. + +```lua +Config.OpenType.Items = { + ["radio"] = {}, + + ["police_radio"] = { + label = "Police Radio", + codeplug = { + allowOtherChannels = false, + channels = { + { frequency = 911, label = "Police - Main" }, + { frequency = 921, label = "Police - Tactical" }, + }, + }, + }, +} +``` + +Each key is an item name. Each item is its own radio: carrying an item does nothing until the player uses it. A player can have several radios open at once and hears all of them. Dropping one radio only disconnects the channels that radio joined. + +A **codeplug** turns an item into a locked radio, like a department radio: + +- A player using that item can only join the channels in its `channels` list. +- Those channels can only be joined by players using that item. The channel's normal access rules (jobs and so on) are ignored for them. +- A plain `radio` can't tune to those channels. +- Anyone who uses the item gets access, whatever their job. Only using the item counts, not carrying it. + +| Field | Default | Description | +| ----------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `label` | none | Name shown in the radio for codeplug radios. | +| `codeplug.channels` | none | List of `{ frequency, label }`. The channels don't have to exist in `/radioadmin`. If they do, their settings (label, no-talk-over and so on) still apply. | +| `codeplug.allowOtherChannels` | `false` | When `true`, the radio can also join any other channel the player has normal access to. The codeplug channels stay locked to the item. | + +Every item here must also exist in your inventory. See [Add the Items](/radio/installationguide#add-the-items). + + + The commented-out examples in `configs/opentype.lua` use frequencies 911 to + 917. If you uncomment them, those frequencies become codeplug-only. For + example, `sql/!data.sql` uses 912 for EMS and 913 for Fire, and the example + police radio claims 912 and 913. Pick frequencies that no other group uses. + + +--- + +## Ambient Sound Capture (`configs/audio/ambient.lua`) + +Nearby gunshots and aircraft are mixed into a player's transmission while they talk. + +| Option | Default | Description | +| ------------------ | ------- | ------------------------------------------------------------------------------------------------------- | +| `Enabled` | `true` | Turns ambient capture on or off. | +| `Volume` | `35` | Volume of the captured sounds, from 0 to 100. | +| `LowPass` | `2000` | Low-pass filter in Hz. | +| `HighPass` | `120` | High-pass filter in Hz. | +| `ShotRadius` | `60.0` | How far away (meters) a gunshot can be and still be picked up. | +| `AircraftRadius` | `100.0` | How far away (meters) aircraft are picked up. | +| `ShotCooldown` | `80` | Minimum time (ms) between picked-up shots from the same shooter. | +| `WeaponCategories` | table | Which gunshot sound (`pistol`, `rifle`, `shotgun`, `sniper`) each weapon uses. Add custom weapons here. | + +All options are in `Config.AmbientCapture`. + +--- + +## Push-to-Talk Sounds (`configs/audio/micclicks.lua`) + +`Config.PTTAudioCues` controls the clicks and squelch sounds around a transmission. + +| Option | Default | Description | +| ----------------- | ----------------- | ---------------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | Master switch for the transmit sounds. | +| `localKeying` | `true` | Plays a sound when you start and stop transmitting. Players can still turn this off in their own settings. | +| `natoBeep` | `false` | Plays a short "over" beep when you release push-to-talk. | +| `natoBeepSound` | `"ptt_nato_beep"` | The sound used for the beep. | +| `defaultSoundSet` | `"default"` | The sound set used on channels that don't have their own. Must match an `id` in `soundSets`. | +| `soundSets` | two sets | The sound sets you can choose from. zerio-radio ships with `default` and `subtle`. | + +You can pick a different sound set per channel in `/radioadmin` (channel editor, "Mic Click Sound"). + +### Custom Sound Sets + +Each sound set names four sounds. A sound name is a file in `html/sounds/` without `.ogg`. + +```lua +{ + id = "radio2", + label = "Radio 2", + txStart = "radio2_tx_start", -- you start transmitting + txEnd = "radio2_tx_end", -- you stop transmitting + rxOpen = "radio2_rx_open", -- someone else starts talking + rxClose = nil, -- someone else stops talking (nil = no sound) +} +``` + +To add one: + +1. Put your `.ogg` files in `html/sounds/`, for example `html/sounds/radio2_tx_start.ogg`. The `fxmanifest.lua` already includes every `.ogg` in that folder. +2. Add the set to `soundSets`. +3. Restart zerio-radio and pick the set as `defaultSoundSet` or per channel. + +--- + +## Signal Quality (`configs/audio/submix.lua`) + +These settings decide how a weak signal sounds. The signal itself comes from towers, jammers and the other features below. + +### General + +| Option | Default | Description | +| ---------------------------- | ------- | ------------------------------------------------ | +| `Config.SubmixCheckInterval` | `2000` | How often (ms) the signal bars in the UI update. | + +### Signal Degradation + +`Config.SignalDegradation`: + +| Option | Default | Description | +| --------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | When `false`, audio quality doesn't change with signal and the signal bars always show full. | +| `curve` | `"exponential"` | How quality falls as signal drops. `"linear"`: in proportion. `"exponential"`: slowly at first, then fast near zero. `"stepped"`: jumps between the tiers below. | +| `exponent` | `1.5` | Strength of the `"exponential"` curve. | +| `transmitterSide` | `true` | Listeners hear the worse of their own signal and the talker's signal. When `false`, only the listener's signal counts. | +| `transmitterUpdateInterval` | `2500` | How often (ms) a talker's signal is sent to listeners. | + +`breakingUp` makes audio drop out in short bursts when signal is very low: + +| Option | Default | Description | +| ----------------------------- | -------------- | --------------------------------------------------------------- | +| `enabled` | `true` | Turns breaking up on or off. | +| `threshold` | `0.30` | Signal level (0.0 to 1.0) below which audio starts breaking up. | +| `minGap` / `maxGap` | `400` / `1800` | Time (ms) between dropouts. | +| `minDuration` / `maxDuration` | `60` / `220` | Length (ms) of each dropout. | + +`staticBurst` plays a sound when a talker's signal drops to zero (`sound`, default `"static_burst"`) and when it comes back (`reconnectSound`, default `"reconnect_click"`). Set `enabled = false` to turn it off. + +### Quality Tiers + +`Config.SubmixTiers` defines how the audio sounds at each signal level. This is advanced tuning; most servers leave it alone. + +| Field | Description | +| -------------- | ------------------------------------------------------------------- | +| `name` | Tier name, for your reference. | +| `minSignal` | The listener needs at least this signal (0.0 to 1.0) for this tier. | +| `freq_low` | Low-cut frequency in Hz. Higher removes more bass. | +| `freq_hi` | High-cut frequency in Hz. Lower makes speech less clear. | +| `rm_mix` | Distortion amount, from 0.0 (clean) to 1.0. | +| `rm_mod_freq` | Distortion frequency in Hz. `0` turns it off. | +| `volume` | Fraction of the player's radio volume (1.0 = full). | +| `static_level` | Amount of background static. | + +The default tiers are Excellent (0.80 and up), Good (0.60), Fair (0.40), Poor (0.20) and Dead (0.00). + +--- + +## Radio Towers (`configs/features/towers.lua`) + +Towers give radio coverage. Each tower has a position, a range, a power and the frequencies it serves. You create towers in `/radioadmin` (Towers page), and `sql/!data.sql` adds nine examples. Channels with no tower nearby get `defaultSignal`. + +### Coverage + +| Option | Default | Description | +| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `enabled` | see below | Turns tower coverage on. When `false`, everyone has full signal (other features like jammers and signal zones still apply). | +| `defaultSignal` | `0.0` | Signal (0.0 to 1.0) where no tower covers the player. `0.0` means no signal; `1.0` means full signal everywhere. | +| `checkInterval` | `2000` | How often (ms) each player's signal is recalculated. | +| `coreRadiusFraction` | `0.5` | The inner part of a tower's range that gets full signal. `0.5` means the inner half; signal falls off linearly beyond that. `0.0` falls off from the center. | + + + Check `enabled` in your copy of the file. With towers enabled, `defaultSignal + = 0.0` and no towers in the database, nobody has signal and push-to-talk is + blocked with "No signal". Add towers in `/radioadmin` or with `sql/!data.sql`, + set `enabled = false`, or raise `defaultSignal`. + + +### Destructible Towers + +`health` lets players shoot or blow up tower masts. A damaged tower gives weaker signal, and a destroyed one gives none until it is repaired. + +This only applies to towers that have their own `prop` and don't use a world prop (the "world prop" option in the tower editor points at a mast that is already part of the map). Towers using a world prop can't be destroyed. + +| Option | Default | Description | +| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | When `false`, every tower is indestructible. | +| `max` | `250` | Starting health. | +| `smokeAt` | `150` | Health at which the mast starts smoking. | +| `fireAt` | `60` | Health at which the smoke thickens and sparks appear. | +| `damage` | table | Damage per hit: `bullet = 8`, `melee = 0`, `explosive = 250`. | +| `downtime` | `600000` | How long (ms) a destroyed tower stays down before it repairs itself (10 minutes). `0` keeps it down until someone repairs it. | + +### In-Game Repair + +`health.repair` lets a player walk up to a destroyed mast and repair it. + +| Option | Default | Description | +| -------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | When `false`, towers only come back after `downtime` or through the `RepairTower` export. | +| `interaction.type` | `"proximity"` | `"proximity"` shows a prompt and uses a key. `"target"` adds an option to your target script. | +| `interaction.targetScript` | `"ox_target"` | `"ox_target"` or `"qb-target"`. Only used with `"target"`. | +| `interaction.distance` | `2.5` | How close (meters) the player must be. | +| `interaction.key` | `38` | Key for proximity mode. `38` is E. | +| `item` | `"radio_repair_kit"` | Item needed to repair. Set to `nil` or `false` to let anyone repair without an item. Not used in standalone. | +| `consumeItem` | `true` | Removes the item after a successful repair. | +| `duration` | `8000` | How long (ms) the repair takes. Moving away, dying or getting in a vehicle cancels it. `0` repairs instantly. | +| `requiredAce` | `nil` | An ace the player needs to repair. `nil` lets anyone with the item repair. | + +--- + +## Panic Button (`configs/features/panic.lua`) + +```lua +Config.EnablePanicButton = true +Config.PanicButtonCooldown = 5 + +Config.PanicAlertReceive = { + Notify = true, +} +``` + +| Option | Default | Description | +| ---------------------------- | ------- | ------------------------------------------------------------------------------------------------- | +| `Config.EnablePanicButton` | `true` | Shows the panic button on the radio. | +| `Config.PanicButtonCooldown` | `5` | Seconds a player must wait between panic presses. | +| `PanicAlertReceive.Notify` | `true` | Also shows a notification to players who receive the alert, so they see it with the radio closed. | + +Players who receive a panic alert hear `html/sounds/panic_alert.ogg` and see a screen effect. Players can turn the screen effect off with the "Emergency Alerts" setting in the radio. To use your own sound, replace that file with your own `.ogg` file of the same name. + +When unit GPS is on for the channel, the player's blip also flashes (see `FlashOnPanic` in [Unit GPS](#unit-gps-configsfeaturesgpslua)). + +Every panic alert also shows up in the dispatch console. To send it to zerio-dispatch, ps-dispatch or lb-tablet as well, see [Dispatch Scripts](#dispatch-scripts-configsfeaturesdispatchlua). + +--- + +## Dispatch Scripts (`configs/features/dispatch.lua`) + +Panic and man-down alerts always reach the dispatch console. These settings also send them to a third-party dispatch script. + +```lua +Config.DispatchAlerts = { + Script = "auto", + Jobs = { "police" }, + Alerts = { + panic = { Enabled = true, Code = "10-33" }, + mandown = { Enabled = false, Code = "10-99" }, + }, +} +``` + +| Option | Default | Description | +| ---------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Script` | `"auto"` | `"auto"` uses the first one running of `zerio-dispatch`, `lb-tablet` and `ps-dispatch`. Set one of those names to pick it, or `"none"` to send nothing. | +| `Jobs` | `{ "police" }` | Jobs that receive the alerts in ps-dispatch and lb-tablet. ps-dispatch also accepts its job types, such as `"leo"`. | +| `Alerts.panic` | on, `"10-33"` | Sends panic button presses, with the code the alert shows. | +| `Alerts.mandown` | off, `"10-99"` | Sends man-down alerts once the countdown runs out, on channels with man-down turned on. | + +The alert is only sent once the server has accepted it, so the panic cooldown also applies to the dispatch script. + +To support another dispatch script, edit `Functions.SendDispatchAlert` in `server/functions.lua` (for scripts that take alerts on the server) and `client/functions.lua` (for scripts that take alerts from the player's client). Or listen to the [`dispatchAlert`](/radio/devdocs#dispatchalert) event from your own resource. + +--- + +## Radio Scanner (`configs/features/scanner.lua`) + +The scanner lists active channels near the player and lets them lock on to listen. + +```lua +Config.Scanner = { + OpenType = "command", + Item = "radio_scanner", + Keybind = "f6", + Command = "radioscanner", + -- ... +} +``` + +| Option | Default | Description | +| ------------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `OpenType` | `"command"` | How players open the scanner: `"item"`, `"command"`, `"keybind"` or `"custom"` (the `OpenScanner` export or the `zerio-radio:client:openscanner` event). One value, not a list. | +| `Item` | `"radio_scanner"` | The scanner item, for `"item"`. | +| `Keybind` | `"f6"` | The default key, for `"keybind"`. | +| `Command` | `"radioscanner"` | The command, for `"command"`. | +| `Distance` | `500` | How far away (meters) a transmitting player can be and still show up. | +| `Time` | `{ min = 5, max = 5 }` | How long a scan takes, in seconds. A random time between `min` and `max` is used. | +| `HiddenChannels` | `{ 911 }` | Frequencies that never show up in scans. | +| `FrequencyRange` | `nil` | Limit scans to a range, for example `{ min = 100, max = 500 }`. `nil` scans all frequencies. | +| `BackgroundScan` | `true` | While the player is on a channel, checks for nearby active transmissions in the background and shows a notification. | +| `BackgroundScanInterval` | `30` | Seconds between background scans. | +| `LockOn` | `true` | Lets players lock on to a scanned channel and hear it. | +| `LockOnSilenceSeconds` | `3` | The lock-on ends after this many seconds without anyone talking. | + +--- + +## Animations and Props (`configs/features/animation.lua`) + +`Config.Animation` sets the radio prop in the player's hand and the animations for opening the radio and talking. + +```lua +Config.Animation = { + OpenProp = { + Model = GetHashKey("zerio_radio"), + Bone = 28422, + Offset = vector3(0.0, 0.0, 0.0), + Rotation = vector3(90.0, 90.0, 0.0), + }, + + TalkProp = { + Model = GetHashKey("zerio_radio"), + Bone = 60309, + Offset = vector3(0.05, 0.0, 0.015), + Rotation = vector3(0.0, 90.0, -45.0), + }, + + OpenAnimation = { + Normal = { Dictionary = "cellphone@", Name = "cellphone_text_in" }, + InCar = { Dictionary = "cellphone@in_car@ds", Name = "cellphone_text_in" }, + }, + + TalkAnimation = { + Name = "generic_radio_chatter", + Dictionary = "random@arrests", + StopSpeed = 1.0, + }, + + ClothingAnimations = {}, +} +``` + +| Block | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `OpenProp` | The prop held while the radio is open. `Model` is the prop, `Bone` the ped bone ID, `Offset` and `Rotation` its position on the bone. | +| `TalkProp` | The prop held while talking. | +| `OpenAnimation` | The animation while the radio is open: `Normal` on foot, `InCar` in a vehicle. | +| `TalkAnimation` | The animation while holding push-to-talk. `StopSpeed` (1.0 to 8.0, keep the `.0`) sets how fast it stops; 8.0 is instant. | +| `ClothingAnimations` | Talk animations used instead of `TalkAnimation` when the player wears certain clothing. | + +Remove a block (or set it to `nil`) to turn it off. The `zerio_radio` model comes from the `zerio-radio-prop` resource. Bone IDs are listed on the [RAGE:MP wiki](https://wiki.rage.mp/index.php?title=Bones). + +See [Animations & Props](/radio/faq#animations--props) in the FAQ for examples, including clothing-based animations. + +--- + +## Audio Leak (`configs/features/audio-leak.lua`) + +Players standing near someone on the radio hear that radio's audio from the world, unless the radio user wears an earpiece. All options are in `Config.RadioLeak`. + +| Option | Default | Description | +| ----------------------------------- | ------- | ------------------------------------------------------------------------------ | +| `enabled` | `true` | Turns audio leak on or off. | +| `range` | `5.0` | How far away (meters) nearby players hear the radio. | +| `maxVolume` | `0.8` | Volume up close. | +| `fadeStart` | `1.5` | Distance (meters) where the volume starts to fade. It reaches zero at `range`. | +| `vehicleRadioUser.enabled` | `true` | Makes the leak quieter when the radio user is in a vehicle. | +| `vehicleRadioUser.volumeMultiplier` | `0.35` | Volume multiplier in a vehicle. | +| `maxChannelsPerPlayer` | `8` | Maximum channels per player that can leak. A safety cap. | +| `checkInterval` | `1500` | How often (ms) nearby players are checked. | +| `volumeInterval` | `500` | How often (ms) leak volume is updated. | + +### Earpieces + +`earpiece` turns the leak off for players wearing an earpiece. Set `earpiece.enabled = false` to ignore earpieces. + +- `props`: Earpiece props, as `{ component = 2, index = 2 }`. `component` is the prop slot and `index` the prop drawable. +- `drawables`: Earpiece clothing, as `{ component = 8, drawable = 15 }`. + +Both accept an optional `texture` to match only one texture. Add the IDs of the earpieces on your server's clothing packs. + +### Proximity Pickup + +`proximityPickup` mixes the voices of nearby players into your transmission while you talk, so people around you can be heard on the radio. + +| Option | Default | Description | +| --------------- | ------- | --------------------------------------------------------- | +| `enabled` | `true` | Turns proximity pickup on or off. | +| `range` | `2.5` | How close (meters) a player must be to be picked up. | +| `checkInterval` | `1000` | How often (ms) nearby players are checked while you talk. | +| `maxBystanders` | `3` | Maximum number of nearby players picked up at once. | + +--- + +## Jammers (`configs/features/jammers.lua`) + +Jammers lower or remove signal in an area. Ground jammers are placed in the world. Personal jammers are worn on the player's back and move with them. + +| Option | Default | Description | +| --------------- | ---------------- | ------------------------------------------------------------------------------------------- | +| `enabled` | `true` | Turns jammers on or off. | +| `maxActive` | `5` | Maximum active jammers (ground and personal together) on the server. `0` removes the limit. | +| `standaloneAce` | `"zerio.jammer"` | The ace needed for the jammer commands (`/placejammer` and so on) on every framework. | + +### Ground Jammers + +`groundTypes` is a list of ground jammer types. Using the item starts placement mode: a preview follows your crosshair and you confirm the spot. Add more entries for more tiers; each `id` must be unique. + +| Field | Default | Description | +| ------------------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| `id` | `"standard"` | Unique ID. Also used by `/placejammer [type]`. | +| `label` | `"Radio Jammer"` | Name shown in lists. | +| `enabled` | `true` | Turns this type on or off. | +| `item` | `"radio_jammer"` | Item that places this jammer. Not used in standalone. | +| `radius` | `150.0` | Jamming radius in meters. | +| `power` | `1.0` | How much signal it removes, from 0.0 (none) to 1.0 (all). | +| `prop` | `"reh_prop_reh_jammer_01a"` | The jammer prop. | +| `frequencies` | `"all"` | `"all"`, or a list of frequencies like `{ 911, 420 }`. | +| `consumeOnPlace` | `true` | Removes the item when the jammer is placed. | +| `health` | table | Same as tower health: `enabled`, `max = 100`, `smokeAt = 60`, `fireAt = 25`, `damage = { bullet = 15, melee = 8, explosive = 80 }`. | +| `pickupMinHealth` | `80` | Minimum health (percent) for the owner to pick the jammer back up. | +| `interaction` | table | How the owner picks it up: `type` (`"proximity"` or `"target"`), `targetScript`, `distance = 2.0`, `pickupKey = 38` (E). | +| `placeRaycastDist` | `20.0` | How far (meters) the placement preview can reach. | +| `despawnOnLogout` | `true` | Removes the jammer when the player who placed it disconnects. | + +### Personal Jammers + +`personalTypes` is a list of personal jammer types. Using the item turns it on or off. + +| Field | Default | Description | +| ------------- | -------------------------------- | ------------------------------------------------- | +| `id` | `"standard"` | Unique ID. Also used by `/personaljammer [type]`. | +| `label` | `"Mobile Radio Jammer"` | Name shown in lists. | +| `enabled` | `true` | Turns this type on or off. | +| `item` | `"mobile_radio_jammer"` | Item that turns this jammer on and off. | +| `radius` | `50.0` | Jamming radius around the carrier, in meters. | +| `power` | `0.85` | How much signal it removes, from 0.0 to 1.0. | +| `prop` | `"ch_prop_ch_mobile_jammer_01x"` | The prop on the player's back. | +| `bone` | `24816` | The bone the prop is attached to (mid-back). | +| `offset` | `vec3(-0.15, -0.125, 0.0)` | Position of the prop on the bone. | +| `rotation` | `vec3(90.0, 90.0, 0.0)` | Rotation of the prop on the bone. | +| `frequencies` | `"all"` | `"all"`, or a list of frequencies. | + +A player can't place a jammer while they have a repeater placed or are placing one, and the other way around. + +--- + +## Repeaters (`configs/features/repeaters.lua`) + +Repeaters extend tower coverage. A repeater only works if it is placed somewhere that already has tower signal. Once active, it gives full signal at its position, fading out to its range. Players get whichever is stronger: the repeater or their own tower signal. Underground signal loss does not apply to repeater signal. + +| Option | Default | Description | +| --------------- | ------------------ | -------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | Turns repeaters on or off. | +| `maxActive` | `10` | Maximum player-placed repeaters on the server. `0` removes the limit. `/placerepeater` ignores it. | +| `standaloneAce` | `"zerio.repeater"` | The ace needed for the repeater commands on every framework. | + +`types` is a list of repeater types: + +| Field | Default | Description | +| ------------------ | ------------------------ | -------------------------------------------------------------------------------------------- | +| `id` | `"standard"` | Unique ID. Also used by `/placerepeater [type]`. | +| `label` | `"Radio Repeater"` | Name shown in lists and the admin menu. | +| `enabled` | `true` | Turns this type on or off. | +| `item` | `"radio_repeater"` | Item that places this repeater. Not used in standalone. | +| `prop` | `"zerio_radio_repeater"` | The repeater prop (from `zerio-radio-prop`). | +| `range` | `300.0` | Coverage radius in meters. | +| `falloff` | `1.0` | How fast signal fades toward the edge. `1.0` is even; `2.0` drops off sharply near the edge. | +| `frequencies` | `"all"` | `"all"`, or a list of frequencies. | +| `consumeOnPlace` | `true` | Removes the item when the repeater is placed. | +| `despawnOnLogout` | `true` | Removes the repeater when the player who placed it disconnects. | +| `placeRaycastDist` | `20.0` | How far (meters) the placement preview can reach. | +| `interaction` | table | How the owner picks it up: `type`, `targetScript`, `distance = 2.0`, `pickupKey = 38` (E). | + +--- + +## Signal Zones (`configs/features/cutoffzones.lua`) + +```lua +Config.CutoffZones = { + enabled = true, +} +``` + +Signal zones are 3D areas that force the signal inside them to a set level, from 0% (tunnels, bunkers) to 100% (for example a building with its own coverage). The zone's level replaces tower coverage, underground loss, repeaters and weather. Jammers and low battery still apply. When zones overlap, the smallest one wins. + +You draw zones in `/radioadmin` on the Signal Zones page. They are saved in the database. `enabled = false` turns the feature off. + +--- + +## Underground Signal Loss (`configs/features/occlusion.lua`) + +Lowers signal when the player is underground, for example in sewers, mines and tunnels. Ground-level interiors like police stations are not affected. All options are in `Config.TerrainOcclusion`. + +| Option | Default | Description | +| ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `enabled` | `true` | Turns underground signal loss on or off. | +| `undergroundPenalty` | `0.2` | Signal multiplier underground. `0.2` leaves 20% of the signal; `0.0` removes it. | +| `undergroundThreshold` | `2.0` | How many meters below the surrounding ground the player must be to count as underground. Raise it if basements trigger it; lower it if shallow tunnels don't. | +| `checkInterval` | `2000` | How often (ms) it is checked. | +| `moveThreshold` | `20.0` | Checks again right away if the player moves more than this many meters. | + +--- + +## Weather (`configs/features/weather.lua`) + +Lowers signal in bad weather. All options are in `Config.WeatherEffects`. + +| Option | Default | Description | +| --------------- | ------- | ------------------------------------------------------------------------------------------- | +| `enabled` | `true` | Turns weather effects on or off. | +| `checkInterval` | `10000` | How often (ms) the weather is checked. | +| `penalties` | table | Signal penalty per weather type, from 0.0 (no effect) to 1.0 (no signal). | +| `thunderJitter` | `0.10` | During thunder, the penalty randomly moves up and down by this much. `0.0` keeps it steady. | + +Default penalties: `RAIN` 0.08, `THUNDER` 0.28, `BLIZZARD` 0.18, `SNOW` 0.05, `XMAS` 0.05, `SNOWLIGHT` 0.03, `SMOG` 0.02, `FOGGY` 0.02, `OVERCAST` 0.01, and 0.0 for clear weather and `HALLOWEEN`. + +--- + +## Battery (`configs/features/battery.lua`) + +The battery system is off by default. When on, radios drain while in use, and players swap batteries or charge at charging stations. A dead battery can't open the radio, join channels or transmit. + +Where the charge is stored: + +- **ox_inventory or QBCore**, with the radio as an item: on the radio item. The charge stays with the item and shows on ox_inventory's tooltip. +- **ESX's built-in inventory**: in server memory for the session. Not shown on the item. +- **Command, keybind or custom open type**: one battery per player, in server memory. + +If you use ox_inventory, add the `radio_battery` item with its `server.export` when you turn this on (see [Add the Items](/radio/installationguide#add-the-items)). + +### Drain + +| Option | Default | Description | +| ------------------------ | ----------------- | --------------------------------------------------------------------------------- | +| `Enabled` | `false` | Turns the battery system on. | +| `DrainRateMultiplier` | `1.0` | Multiplies all drain rates. | +| `DrainPerSecondIdle` | `0.010` | Percent per second while the radio is on with no channel. | +| `DrainPerSecondListen` | `0.020` | Percent per second while on at least one channel. | +| `DrainPerSecondTransmit` | `0.080` | Percent per second while transmitting. | +| `SaveIntervalMs` | `60000` | How often (ms) the charge is saved to the item. | +| `BatteryItemName` | `"radio_battery"` | The battery item. Using it lets the player pick a radio to swap the battery into. | +| `StartingCharge` | `100` | Charge of a radio that has never had a battery level. | + +### Low Battery Signal Penalty + +`SignalPenalty` weakens the signal as the battery runs low. + +| Option | Default | Description | +| -------------------------- | ------- | --------------------------------------------------------- | +| `Enabled` | `false` | Turns the penalty on. | +| `LowBatteryThreshold` | `20` | Battery percent where the signal starts to drop. | +| `CriticalBatteryThreshold` | `5` | Battery percent where the penalty is at its worst. | +| `LowMultiplier` | `0.5` | Signal multiplier at `LowBatteryThreshold`. | +| `CriticalMultiplier` | `0.1` | Signal multiplier at or below `CriticalBatteryThreshold`. | + +Between the two thresholds the multiplier goes linearly from `LowMultiplier` to `CriticalMultiplier`. + +### Charging Stations + +`Stations` are places where players charge their radio. Players press E (or use the target option) to start charging, stay within the radius, and press E again or walk away to stop. + +| Option | Default | Description | +| -------------------------- | ------------- | ----------------------------------------------------------------------------- | +| `Enabled` | `true` | Turns charging stations on or off. | +| `ChargePerSecond` | `2.0` | Percent charged per second. A station can override it with `chargePerSecond`. | +| `Interaction.type` | `"proximity"` | `"proximity"` (prompt and key) or `"target"`. | +| `Interaction.targetScript` | `"ox_target"` | `"ox_target"` or `"qb-target"`. | +| `Interaction.useKey` | `38` | Key for proximity mode. `38` is E. | +| `ShowMarker` | `true` | Draws a marker at each station. | +| `ShowBlip` | `true` | Adds a map blip for each station. | +| `Marker` | table | Marker `type`, `size` and `color`. | +| `Blip` | table | Blip `sprite = 354`, `color = 2`, `scale = 0.7`, `label = "Radio Charger"`. | +| `Locations` | one example | The stations. Replace the example with your own. | + +```lua +Locations = { + { coords = vector3(441.5, -982.0, 30.7), radius = 2.0 }, + { coords = vector3(831.4, -1289.7, 27.9), radius = 3.0, chargePerSecond = 5.0 }, +}, +``` + +--- + +## Channel Patches (`configs/features/patches.lua`) + +A patch links two or more channels so players on one channel hear the others. Admins manage patches in `/radioadmin` on the Patches page. Patches made in the web dispatch panel are synced to the game when `PortalUrl` is set in `configs/voice.lua`. + +| Option | Default | Description | +| ------------------ | ------- | -------------------------------------------------------------------------------------------------------- | +| `Enabled` | `true` | Turns patches on or off. | +| `DispatchJobs` | `{}` | Jobs that can manage patches without being admins, for example `{ "dispatcher" }`. QBCore, Qbox and ESX. | +| `AcePermissions` | `{}` | Aces that can manage patches, for example `{ "zerio.radio.dispatch" }`. | +| `MaxPatchSize` | `6` | Maximum channels in one patch. | +| `MaxActivePatches` | `20` | Maximum patches on the server at once. | + +Players allowed by `DispatchJobs` or `AcePermissions` open `/radioadmin` and only see the Patches page. + +--- + +## Man-Down (`configs/features/mandown.lua`) + +When a player stays down while on a channel, the radio counts down on their screen and then sends an emergency alert to their channels. Other players see a banner and a flashing area on the map until the player recovers, leaves, or someone clears the alert. + + + Man-down is also turned on per channel. In `/radioadmin`, edit the channel and + turn on "Man-Down". It is off for every channel by default, so nothing happens + until you turn it on. + + +| Option | Default | Description | +| ------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- | +| `Enabled` | `true` | Master switch. | +| `Trigger` | `"either"` | What starts the countdown (see below). | +| `WarningSeconds` | `10` | Seconds from the trigger to the alert. The player can cancel during this time. | +| `CheckIntervalMs` | `1000` | How often (ms) the player's state is checked. | +| `MovementThreshold` | `0.5` | For `"stationary"`: how far the player must move to count as moving. | +| `CancelKeybind` | `"BACK"` | Default key (Backspace) to cancel a false alarm. Players can rebind it. Doesn't cancel if the player is really down. | + +`Trigger` values: + +- `"ragdoll"`: The player is ragdolled. Death systems that keep the player in a "downed" animation (like qb-ambulancejob or esx_ambulancejob) don't count. +- `"dead"`: The player is dead or in last stand according to the framework (QBCore last stand or dead, ESX dead), or dead by game natives on standalone. +- `"either"`: Ragdolled or dead. The default. +- `"stationary"`: The player hasn't moved for the whole countdown. + +`Blip` is the map marker other players see: + +| Option | Default | Description | +| --------------- | ------- | ----------------------------- | +| `Enabled` | `true` | Shows the blip. | +| `Radius` | `30.0` | Radius of the area in meters. | +| `Color` | `1` | Blip color (1 is red). | +| `Alpha` | `130` | Opacity, from 0 to 255. | +| `Flash` | `true` | Makes the blip flash. | +| `FlashInterval` | `500` | Flash speed in ms. | + +--- + +## Unit GPS (`configs/features/gps.lua`) + +Shows live map blips for the other players on your channels. Blips change shape by vehicle type and can flash on sirens and panic. + + + Unit GPS is also turned on per channel. In `/radioadmin`, edit the channel and + turn on "Unit GPS". It is off for every channel by default. + + +| Option | Default | Description | +| ------------------- | --------------- | ---------------------------------------------------------------------------------------------------------- | +| `Enabled` | `true` | Master switch. | +| `UpdateInterval` | `2000` | How often (ms) each player's position is sent. Lower is smoother but uses more network traffic. | +| `BlipColor` | `2` | Blip color. See the [FiveM blip colors](https://docs.fivem.net/docs/game-references/blips/#blip-colors). | +| `BlipScale` | `0.8` | Blip size. | +| `RotateBlips` | `true` | Turns the blip to face the unit's heading. | +| `BlipCategory` | `21` | Groups all radio blips into one line in the pause map legend. `nil` or `false` lists each blip separately. | +| `BlipCategoryLabel` | `"Radio Units"` | Legend name for the category. | +| `AllChannels` | `true` | Shows units from every channel you listen to. When `false`, only your active channel. | +| `ShowLabel` | `true` | Shows the unit's callsign (or name) on the blip. | +| `FlashOnPanic` | `true` | Flashes the blip when that unit presses the panic button. | +| `FlashOnSiren` | `true` | Flashes the blip when that unit's siren is on. | +| `Sprites` | table | Blip sprite per movement type: `foot = 1`, `car = 225`, `bike = 226`, `air = 422`, `boat = 427`. | + +--- + +## Speakers (`configs/features/speakers.lua`) + +Speakers are PA speakers placed in the world. Admins place them in `/radioadmin` on the Speakers page and group them into stations. Speakers need OneSync. + +| Option | Default | Description | +| ------------------ | ------------------- | ------------------------------------------------------------------------------ | +| `enabled` | `true` | Turns speakers on or off. | +| `defaultProp` | `"prop_speaker_01"` | Prop for new speakers. | +| `defaultRadius` | `30.0` | How far (meters) a new speaker can be heard. Each speaker can override it. | +| `propDrawDistance` | `80.0` | Distance (meters) at which speaker props are hidden. Keep it above the radius. | +| `placementProp` | `"prop_speaker_01"` | Preview prop shown while placing. | +| `placeRaycastDist` | `30.0` | How far (meters) the placement preview can reach. | + +--- + +## Translations (`locales/`) + +Texts are in `locales/en.json` and `locales/sv.json`. `Config.Locale` in `configs/main.lua` picks the file. + +- The top-level keys in the JSON file are the in-game notifications and command help. You can edit them, and they change after a restart. +- The `ui` section is the text inside the radio itself. That text is built into the radio's interface, so editing it in the JSON file changes nothing. + +To add a language, copy `en.json` to a new file such as `de.json`, translate it and set `Config.Locale = "de"`. Notifications and command help then use your file. The radio's interface only has English and Swedish built in and shows English for other languages. Contact support if you want your translation added to the radio's interface. diff --git a/pages/radio/description.mdx b/pages/radio/description.mdx index 706b064..015a04b 100644 --- a/pages/radio/description.mdx +++ b/pages/radio/description.mdx @@ -3,7 +3,7 @@ import { VideoIcon } from "@radix-ui/react-icons"; # Description -This is one of our most wellknown resources, for good reasons. Our Radio is really guaranteed to bring your server to the next level. +Zerio Radio v3 is a radio system for FiveM roleplay servers. Players join frequencies, talk with push-to-talk, and deal with signal coverage, jammers, batteries and other radio realism. Radio audio runs on Zerio's hosted voice server (zerio-voice), not through pma-voice or Mumble, so it works next to whatever proximity voice your server already uses.
@@ -14,8 +14,90 @@ This is one of our most wellknown resources, for good reasons. Our Radio is real arrow /> +## Key Features + +### Radio + +- **Frequencies**: Join any frequency, or only the ones you have access to. Listen to several channels at once and pick which one you transmit on. +- **Push-to-talk**: Its own keybind (Left Alt by default), with transmit and receive sounds that you can change per channel. +- **No-talk-over channels**: Only one person can transmit at a time on channels where you turn this on. +- **Member list**: See who is on the channel and who is talking, with callsigns and an optional anonymous mode. +- **Favorites and recents**: Save frequencies and jump back to recently used ones. +- **Private channels**: Players can create temporary, password-protected channels. +- **Messages and contacts**: Send text messages to other players from the radio. +- **Panic button**: Sends an alert with a sound, screen effect and notification, and passes it on to dispatch. +- **Radio scanner**: Scan for nearby active channels and lock on to listen in. +- **Player settings**: Themes, light and dark mode, three radio skins, and movable, resizable overlays. + +### Signal and Realism + +- **Radio towers**: Coverage comes from towers you place. Towers can be shot down and repaired in-game. +- **Repeaters**: Players place repeaters to extend tower coverage. +- **Jammers**: Ground jammers and personal (backpack) jammers that kill signal in an area. +- **Signal zones**: Draw zones in the admin menu that force a signal level, from none (tunnels, bunkers) to full. +- **Underground and weather**: Signal drops underground and in bad weather. +- **Audio degradation**: Weak signal sounds worse and breaks up, both for the listener and the transmitter. +- **Battery**: Optional battery drain, battery swaps and charging stations. +- **Ambient capture**: Nearby gunshots and aircraft can be heard in transmissions. +- **Audio leak**: Players standing next to you hear your radio unless you wear an earpiece. + +### Coordination + +- **Man-down alerts**: Warns, then alerts the channel when a player stays down. Turned on per channel. +- **Unit GPS**: Live map blips for players on the same channel. Turned on per channel. +- **Channel patches**: Link channels so they hear each other. +- **Speakers**: PA speakers placed in the world. +- **Signal 100**: Dispatch can reserve a channel for emergency traffic, so only dispatch can talk on it. +- **Dispatch integrations**: Panic and man-down alerts go to the dispatch console and to zerio-dispatch, ps-dispatch or lb-tablet. Other scripts can hook in through [exports and events](/radio/devdocs). + +### Items and Access + +- **Radio as an item**: Players need the item to use the radio. Dropping or losing it disconnects them. +- **Codeplug radios**: Items like a police radio that only work on a fixed set of channels, and are the only way onto those channels. +- **Access rules**: Restrict channels by job, gang (QBCore, rcore_gangs, op-crime), ace permission, identifier or Discord role. + +### Administration + +- **In-game admin menu** (`/radioadmin`): Manage channels, players, towers, repeaters, jammers, signal zones, speakers and patches, and view a signal map. +- **Customer portal**: Set up and manage your voice server at [portal.radio.zerio-scripts.com](https://portal.radio.zerio-scripts.com). The portal also has a web dispatch panel. + ## Requirements -- Voice script: pma-voice, mumble-voip, tokovoip-fivem, saltychat-fivem -- SQL resource: oxmysql, ghmattimysql, mysql-async -- Framework: None **required**, but QBCore & ESX supported +### Essential + +- **Database**: oxmysql. Other SQL resources (mysql-async, ghmattimysql) are not supported. +- **ox_lib**: Used for keybinds, notifications, interaction prompts and callbacks. +- **zerio-radio-prop**: Streams the radio, repeater and vehicle radio props. It is included in the download and runs as its own resource. +- **OneSync**: Must be enabled. +- **Zerio voice server**: Hosted by Zerio. You set it up from the link in your purchase email. +- **Framework**: QBCore, Qbox, ESX, or none (standalone). + +### Optional Dependencies + +- **ox_target or qb-target**: Only if you switch jammer, repeater, tower repair or charging station interactions to `"target"`. +- **zdiscord or Badger_Discord_API**: For channels restricted by Discord role. +- **rcore_gangs or op-crime**: For channels restricted to those gang systems. +- **zerio-dispatch, ps-dispatch or lb-tablet**: Panic and man-down alerts can also be sent to one of these. + +## Compatibility + +### Frameworks + +- **QBCore** and **Qbox**: Detected automatically. Qbox runs through its QBCore compatibility layer. +- **ESX**: Detected automatically. +- **Standalone**: Used when no framework is found. There are no items; players open the radio with a command or keybind, and admin features use ace permissions. + +### Inventories + +- **ox_inventory**: Version 2.47.0 or newer. +- **qb-inventory**: Through QBCore's item functions. +- **ESX inventory**: ESX's built-in inventory. + +### Voice + +- zerio-radio does not replace your proximity voice. pma-voice, or any other proximity voice resource, keeps handling normal voice chat. +- zerio-radio turns off pma-voice's own radio, radio UI and radio animation on start, so they don't overlap. + +### Hardened Servers + +zerio-radio works with every setting of `sv_entityLockdown`, `sv_filterRequestControl` and `sv_stateBagStrictMode true`. diff --git a/pages/radio/devdocs.mdx b/pages/radio/devdocs.mdx index 2834792..db0ded3 100644 --- a/pages/radio/devdocs.mdx +++ b/pages/radio/devdocs.mdx @@ -2,58 +2,92 @@ import { Callout } from "nextra/components"; # Developer Documentation -Information you as a developer, wanting to extend the resource might want to know will be listed here. +Everything other resources can use from zerio-radio: exports, the server events it triggers, and the commands it registers. -Such as notable exports, or such. + + Exports of a feature that's turned off in the config still exist. They do + nothing and return `false`, `nil` or an empty table, so you don't have to + check the config before calling them. + -If empty, then nothing notable exists at the moment. +Frequencies are numbers, such as `100` or `100.5`. Exports that take a frequency also accept it as a string. -## Export list +## Server-Side Exports -### Client +### Players and Channels -#### HasAccess +#### GetPlayerChannels ```lua -local access = exports["zerio-radio"]:HasAccess(frequency) +local channels = exports["zerio-radio"]:GetPlayerChannels(source) +-- { active = 911, listening = { 100, 911 } } ``` -This export can be used to check whether the local player has access to a specific channel. The only parameter it takes is the frequency number. +Returns the channels a player is on. `active` is the channel they talk on, or `0` when they're on none. `listening` lists every channel they're on, including the active one. -It returns a boolean (true/false) to show whether the player had access or not. +#### GetChannelMembers -#### ChangeChannel +```lua +local members = exports["zerio-radio"]:GetChannelMembers(frequency) +``` + +Returns the server ids of everyone on a channel. Players who only listen in through a scanner lock-on aren't members, so they're left out. + +#### IsPlayerTransmitting ```lua -local changed = exports["zerio-radio"]:ChangeChannel(frequency) +local talking = exports["zerio-radio"]:IsPlayerTransmitting(source) ``` -This export can be used to change the frequency of the local player. It takes in the frequency number as a parameter. +Returns `true` while the player is talking on their radio. To react the moment it changes, listen to [`playerStartedTransmitting`](#playerstartedtransmitting) and [`playerStoppedTransmitting`](#playerstoppedtransmitting) instead. + +#### GetPlayerSignalStrength + +```lua +local signal = exports["zerio-radio"]:GetPlayerSignalStrength(source) +local signal = exports["zerio-radio"]:GetPlayerSignalStrength(source, frequency) +``` -It returns whether the player could connect, or not. If false is returned, then the player most likely doesnt have access to the channel. +Returns the player's signal on a channel, from `0.0` (no signal) to `1.0` (full signal), in steps of `0.05`. Without a frequency it uses the channel they talk on. Returns `nil` when they aren't on that channel. + +Signal depends on towers, terrain, weather, jammers, repeaters, signal zones and battery. With all of those turned off, it's always `1.0`. - Disconnecting a player from the radio would be done by setting their radio - frequency to 0. + Signal is worked out by each player's own game and reported to the server when + it changes, so a modified client could report it wrongly. Don't use it for + anything a cheater would gain from. -#### GetCallsign +#### ForceJoinChannel ```lua -local callsign = exports["zerio-radio"]:GetCallsign() +local joined = exports["zerio-radio"]:ForceJoinChannel(source, frequency, setActive) ``` -This export returns the local player's current callsign as a string. If the player has no callsign set, an empty string is returned. +Adds a player to a channel without checking their access (jobs, codeplug radios and so on), like the admin menu's force join. Returns whether they were added. -#### SetCallsign +It still won't add a player to a channel an admin has locked, when their battery is dead or when they're already on `Config.MaxChannels` channels. The channel becomes the one they talk on when `setActive` is `true`, or when they weren't talking on any channel. + +#### ForceLeaveChannel ```lua -exports["zerio-radio"]:SetCallsign(callsign) +local left = exports["zerio-radio"]:ForceLeaveChannel(source, frequency) +local left = exports["zerio-radio"]:ForceLeaveChannel(source) ``` -This export sets the local player's callsign. The change is replicated to the server and persisted in the database. If the player is currently in a radio channel, other members on the same frequency will see the updated callsign in real-time. +Removes a player from a channel, or from every channel when you leave out the frequency. Returns `false` when they weren't on it. -### Server +#### HasAccess + +```lua +local access = exports["zerio-radio"]:HasAccess(frequency, source) +``` + +Returns whether a player may join a channel, going by the channel's access rules and admin lock. Note that the frequency comes first. A frequency without a channel set up is open to everyone, so it returns `true`. + +### Channel Management + +Channels are created and edited in the admin menu (`/radioadmin`) and stored in the database. The exports below only change the running server: the change is gone after a restart, and players already on a channel aren't checked again. #### DoesChannelExist @@ -61,22 +95,34 @@ This export sets the local player's callsign. The change is replicated to the se local exists = exports["zerio-radio"]:DoesChannelExist(frequency) ``` -This export returns whether a channel exists or not in zerio-radio. It not existing, would more specifically entail that there is no restrictions on it / that it hasnt been registered via the `CreateChannel` export or `zerio-radio/shared/channels.lua` file. +Returns whether a channel is set up on that frequency. - A channel not existing does **not** mean that players cant connect to it. - Generally voice scripts allow the player to connect to any valid number. + A frequency without a channel isn't blocked: anyone can join it. A channel + only restricts who can join. #### CreateChannel ```lua -local created = exports["zerio-radio"]:CreateChannel(frequency, data) +local created = exports["zerio-radio"]:CreateChannel(frequency, { + label = "SWAT", + jobs = { "police" }, + noTalkOver = true, +}) ``` -This export can be used to programatically create channels in zerio-radio, just like you would in the `zerio-radio/shared/channels.lua` file. +Sets up a channel until the next restart. Returns `false` when the frequency already has a channel or `label` is missing. -The `data` parameter would resemble the structure of a channel, explained better in `zerio-radio/shared/channels.lua` +| Field | Type | Description | +| ---------------- | --------- | -------------------------------------------------- | +| `label` | `string` | Required. The name players see | +| `jobs` | `table` | Job names that may join | +| `gangs` | `table` | Gang names that may join | +| `discordRoles` | `table` | Discord role ids that may join | +| `acePermissions` | `table` | Ace permissions that may join | +| `whitelisted` | `table` | Player identifiers that may join | +| `noTalkOver` | `boolean` | Only one player can talk at a time on this channel | #### RemoveChannel @@ -84,27 +130,15 @@ The `data` parameter would resemble the structure of a channel, explained better local removed = exports["zerio-radio"]:RemoveChannel(frequency) ``` -This export would remove a channel from zerio-radio. Or, in other words, make it fully "unlocked" meaning anyone can join it. - -#### UpdateSetting - -```lua -exports["zerio-radio"]:UpdateSetting(identifier, key, value) -exports["zerio-radio"]:UpdateSetting(source, key, value) -``` - -This export updates the value of a setting for a specific player. You can either use the players identifier, or their source. -The value keys/settings are: size, volume, callsign, theme, useMouse, anonymous, allowDrag, memberList, positionX, positionY, position2X, position2Y. - -You can have a look at the database table `zerio_radio_settings` for further information about the valid/possible values, or open a ticket in our community server. +Removes a channel until the next restart, which leaves the frequency open to everyone. #### UpdateChannelLabel ```lua -local success = exports["zerio-radio"]:UpdateChannelLabel(frequency, label) +local updated = exports["zerio-radio"]:UpdateChannelLabel(frequency, label) ``` -This export would update the label of a radio channel. +Renames a channel until the next restart. Players on it see the new name straight away. #### GiveAccessToChannel @@ -112,17 +146,19 @@ This export would update the label of a radio channel. local success = exports["zerio-radio"]:GiveAccessToChannel(frequency, accessType, value) ``` -This export would grant a new value access to a specific channel. -The `accessType` would be the kind of access you wish to add, forexample `"jobs"`, `"discordRoles"` or so on. +Adds a value to one of a channel's access rules, for example `GiveAccessToChannel(911, "jobs", "sheriff")`. + +The access types are `jobs`, `gangs`, `rcoreGangs`, `opCrimeGangs`, `discordRoles`, `acePermissions` and `whitelisted`. #### RemoveAccessFromChannel ```lua -local removed = exports["zerio-radio"]:RemoveAccessFromChannel(frequency, accessType, value) +local success = exports["zerio-radio"]:RemoveAccessFromChannel(frequency, accessType, value) ``` -This export would remove access to a specific value from the channel. Or in other words, forexample removing a specific job's, identifier's or such's access. -The `accessType` would be the kind of access you wish to add, forexample `"jobs"`, `"discordRoles"` or so on. +Removes a value from one of a channel's access rules. Returns `false` only when the channel doesn't exist. + +### Callsigns and Settings #### GetCallsign @@ -131,9 +167,7 @@ local callsign = exports["zerio-radio"]:GetCallsign(source) local callsign = exports["zerio-radio"]:GetCallsign(identifier) ``` -This export returns the callsign of a specific player. You can pass either the player's server source (number) or their identifier (string). - -If the player has no callsign set, an empty string is returned. +Returns a player's callsign, from their server id or their identifier. Returns an empty string when they have none. #### SetCallsign @@ -142,11 +176,405 @@ exports["zerio-radio"]:SetCallsign(source, callsign) exports["zerio-radio"]:SetCallsign(identifier, callsign) ``` -This export sets the callsign of a specific player. You can pass either the player's server source (number) or their identifier (string). The change is persisted in the database. +Sets a player's callsign and saves it. Players on the same channels see the new callsign straight away. + +#### UpdateSetting + +```lua +exports["zerio-radio"]:UpdateSetting(source, key, value) +exports["zerio-radio"]:UpdateSetting(identifier, key, value) +``` + +Changes one of a player's radio settings and saves it. The keys are: + +- **General:** `volume`, `callsign`, `theme`, `colorTheme`, `radioSkin`, `streamerMode`, `anonymous`, `pttEnabled`, `debugMode` +- **Layout:** `radioPositionX`, `radioPositionY`, `radioScale`, `memberList`, `memberListShowAll`, `memberListPositionX`, `memberListPositionY`, `memberListScale`, `channelHudPositionX`, `channelHudPositionY`, `channelHudScale` +- **Notifications:** `messageNotifications`, `notificationSound`, `emergencyAlerts`, `joinLeaveAlerts` +- **Audio:** `clickVolume`, `staticNoise`, `memberJoinSound`, `memberLeaveSound` + +The `zerio_radio_settings` table shows the possible values. + +### Codeplug Radios + +Codeplug radios are radio items locked to preset channels, set up in `Config.OpenType.Items` (`configs/opentype.lua`). + +#### GetPlayerCodeplug + +```lua +local codeplug = exports["zerio-radio"]:GetPlayerCodeplug(source) +``` + +Returns the codeplug radio the player is using, or `nil` when the radio they're using isn't one. The table has `itemName`, `label`, `channels` (the preset channels) and `allowOtherChannels`. + +#### PlayerCodeplugCoversFrequency + +```lua +local covered = exports["zerio-radio"]:PlayerCodeplugCoversFrequency(source, frequency) +``` + +Returns whether any codeplug radio the player has open has that frequency as a preset. + +#### CheckAccessOnInventoryChange + +```lua +exports["zerio-radio"]:CheckAccessOnInventoryChange(source) +``` + +Checks the player's access to every channel they're on again, and removes them from the ones they've lost access to. zerio-radio already does this for job changes and radio items, so you only need it when your own script changes what a player can access. + +### Towers + +These exports only change the running server. Use the admin menu for permanent changes. + +#### AddTower + +```lua +local id = exports["zerio-radio"]:AddTower({ + id = "event_relay", + label = "Event relay truck", + position = vector3(215.0, -810.0, 30.7), + range = 800.0, + power = 1.0, +}) +``` + +Adds a tower until the next restart, for example a relay truck during an event. Returns its id, or `nil` when `id` or `position` is missing or the id is taken. + +| Field | Type | Description | +| ------------- | ------------------ | -------------------------------------------------------------------------- | +| `id` | `string` | Required. Unique id | +| `position` | `vector3` | Required | +| `label` | `string` | Shown in the admin menu. Defaults to the id | +| `range` | `number` | Coverage radius. Defaults to `1000.0` | +| `power` | `number` | Signal at the tower, `0.0` to `1.0`. Defaults to `1.0` | +| `frequencies` | `"all"` or `table` | The frequencies it covers. Defaults to `"all"` | +| `prop` | `string` | Model to spawn. With tower health on, a tower with a prop can be destroyed | +| `worldProp` | `boolean` | `true` when the model is already part of the map | +| `enabled` | `boolean` | Defaults to `true` | + +#### RemoveTower + +```lua +local removed = exports["zerio-radio"]:RemoveTower(towerId) +``` + +Removes a tower until the next restart. It stays in the database. + +#### SetTowerEnabled + +```lua +local changed = exports["zerio-radio"]:SetTowerEnabled(towerId, enabled) +``` + +Turns a tower on or off until the next restart. A destroyed tower stays off until it's repaired. + +#### RepairTower + +```lua +local repaired = exports["zerio-radio"]:RepairTower(towerId) +``` + +Restores a damaged or destroyed tower to full health. + +#### GetTowers + +```lua +local towers = exports["zerio-radio"]:GetTowers() +``` + +Returns every tower with `id`, `label`, `position`, `range`, `power`, `frequencies`, `enabled`, `destructible`, `health`, `maxHealth` and `destroyed`. + +### Jammers + +#### PlaceJammer + +```lua +-- A ground jammer +local id = exports["zerio-radio"]:PlaceJammer({ + position = vector3(215.0, -810.0, 30.7), + jammerTypeId = "standard", +}) + +-- A personal jammer, which follows the player +local id = exports["zerio-radio"]:PlaceJammer({ + type = "personal", + serverId = source, +}) +``` + +Places a jammer and returns its id, or `nil` when it couldn't, for example because `Config.Jammers.maxActive` jammers are already placed. + +| Field | Type | Description | +| -------------- | ------------------ | ----------------------------------------------------------------------- | +| `type` | `string` | `"ground"` (default) or `"personal"` | +| `position` | `vector3` | Required for a ground jammer | +| `serverId` | `number` | Required for a personal jammer: the player carrying it | +| `jammerTypeId` | `string` | A type from `Config.Jammers`. Defaults to the first enabled one | +| `radius` | `number` | Overrides the type's radius | +| `power` | `number` | Overrides the type's power, `0.0` to `1.0` | +| `frequencies` | `"all"` or `table` | Overrides the frequencies it jams | +| `placedBy` | `number` | For a ground jammer, the player it counts as placed by. Defaults to `0` | + +A player can carry one personal jammer at a time. + +#### RemoveJammer + +```lua +local removed = exports["zerio-radio"]:RemoveJammer(jammerId) +``` + +#### GetJammers + +```lua +local jammers = exports["zerio-radio"]:GetJammers() +``` + +Returns every jammer with `id`, `type`, `jammerTypeId`, `radius`, `power`, `frequencies` and `placedBy`, plus `position` for ground jammers and `serverId` for personal ones. + +#### PlayerHasActiveJammer + +```lua +local hasJammer = exports["zerio-radio"]:PlayerHasActiveJammer(source) +``` + +Returns whether the player has placed a ground jammer or is carrying a personal one. + +### Repeaters + +#### PlaceRepeater + +```lua +local id = exports["zerio-radio"]:PlaceRepeater({ + position = vector3(215.0, -810.0, 30.7), + typeId = "standard", + range = 300.0, +}) +``` + +Places a repeater and returns its id, or `nil` when it couldn't. A repeater only works within the coverage of a tower. -If the player is currently in a radio channel, other members on the same frequency will see the updated callsign in real-time. +| Field | Type | Description | +| ------------- | ------------------ | ------------------------------------------------------------------------------------------- | +| `position` | `vector3` | Required | +| `typeId` | `string` | A type from `Config.Repeaters`. Defaults to the first enabled one | +| `range` | `number` | Overrides the type's coverage radius | +| `falloff` | `number` | Overrides how fast signal drops towards the edge, `0.1` to `10.0` | +| `frequencies` | `"all"` or `table` | Overrides the frequencies it relays | +| `placedBy` | `number` | The player it counts as placed by. Defaults to `0`, which doesn't count towards `maxActive` | -## Event list +#### RemoveRepeater + +```lua +local removed = exports["zerio-radio"]:RemoveRepeater(repeaterId) +``` + +#### GetRepeaters + +```lua +local repeaters = exports["zerio-radio"]:GetRepeaters() +``` + +Returns every repeater with `id`, `repeaterTypeId`, `label`, `position`, `range`, `falloff`, `frequencies` and `placedBy`. + +#### PlayerHasActiveRepeater + +```lua +local hasRepeater = exports["zerio-radio"]:PlayerHasActiveRepeater(source) +``` + +### Battery + +Battery exports need `Config.Battery.Enabled`, which is off by default. + +#### GetPlayerBattery + +```lua +local battery = exports["zerio-radio"]:GetPlayerBattery(source) +``` + +Returns the battery level of the player's radio as a whole percentage from `0` to `100`, or `nil` for a player who isn't online. `GetBattery(source)` returns the same without rounding. + +#### SetBattery + +```lua +exports["zerio-radio"]:SetBattery(source, level) +``` + +Sets the battery level of the player's radio, from `0` to `100`. At `0` the player is removed from every channel, like with a dead battery. + +### Unit GPS + +GPS only covers channels that have GPS turned on in the admin menu. + +#### GetUnitPositions + +```lua +local units = exports["zerio-radio"]:GetUnitPositions(frequency) +``` + +Returns the position of every member of a channel, for a map in an MDT or CAD. Each entry has: + +| Field | Type | Description | +| ------------- | --------------- | ------------------------------------------------ | +| `source` | `number` | The player's server id | +| `x`, `y`, `z` | `number` | Position | +| `heading` | `number` | | +| `vehicleType` | `string` | `"foot"`, `"car"`, `"bike"`, `"air"` or `"boat"` | +| `sirenOn` | `boolean` | | +| `callsign` | `string` or nil | | +| `name` | `string` or nil | `nil` when the player is anonymous | + +Passengers aren't listed separately: the driver's position stands in for everyone in the vehicle. + +#### GetUnitPosition + +```lua +local unit = exports["zerio-radio"]:GetUnitPosition(source) +``` + +Returns the same entry for one player, or `nil` when they're on no channel with GPS or haven't reported a position yet. + +### Dispatch Console + +These send to the hosted dispatch console. They need the voice server set up in `configs/voice.lua` and `zerio_voice_admin_token` set in your `server.cfg`. + +#### DispatchNotify + +```lua +exports["zerio-radio"]:DispatchNotify({ + title = "911 Call", + body = "Shots fired at Grove Street", + priority = "high", + unit = "1-ADAM-12", + channel = "911", +}) +``` + +Shows a notification to every dispatcher. `priority` is `"low"`, `"medium"` (default), `"high"` or `"critical"`. `"high"` and `"critical"` play a sound. `unit` and `channel` are optional. + +#### DispatchManDownStart + +```lua +exports["zerio-radio"]:DispatchManDownStart({ + playerId = source, + name = "John Doe", + channel = "911", + channelLabel = "Police", + x = 215.0, y = -810.0, z = 30.7, +}) +``` + +Shows a man-down alert to the dispatchers monitoring the channel, with a marker on their map. It stays until `DispatchManDownClear` clears it. zerio-radio's own man-down feature already calls these. + +#### DispatchManDownClear + +```lua +exports["zerio-radio"]:DispatchManDownClear({ playerId = source }) +``` + +### Signal 100 + +While Signal 100 is active on a channel, players can't talk on it and only dispatchers can. Players who press push-to-talk hear the busy tone, and anyone talking when it starts is cut off. Dispatchers start it from the dispatch console; these exports start it from your own scripts. The channel needs Signal 100 turned on in the admin menu. + +#### StartSignal100 + +```lua +local started, reason = exports["zerio-radio"]:StartSignal100(frequency, activatedBy) +``` + +Starts Signal 100 on a channel. `activatedBy` is the name players and dispatchers see, and defaults to `"Dispatch"`. + +Returns `true`, or `false` and a reason: `"not_enabled"` when the channel doesn't allow Signal 100, or `"active_by_dispatch"` when a dispatcher already started it. It waits for the voice server, so call it from a thread or an event handler. + +While a script holds Signal 100, every dispatcher monitoring the channel can talk on it and end it. + +#### EndSignal100 + +```lua +local ended = exports["zerio-radio"]:EndSignal100(frequency) +``` + +Ends Signal 100 on a channel, whoever started it. Returns `true` when it was active. + +#### IsSignal100Active + +```lua +local active = exports["zerio-radio"]:IsSignal100Active(frequency) +``` + +Returns whether Signal 100 is active on a channel. When a dispatcher starts or ends it, this can take up to 5 seconds to catch up. + +## Client-Side Exports + +#### Open + +```lua +exports["zerio-radio"]:Open() +``` + +Opens the radio. ox_inventory calls it as `zerio-radio.Open` when a radio item is used, passing the slot and the item so zerio-radio knows which radio was used. + +#### OpenScanner + +```lua +exports["zerio-radio"]:OpenScanner() +``` + +Opens the radio scanner. + +#### HasAccess + +```lua +local access = exports["zerio-radio"]:HasAccess(frequency) +``` + +Returns whether the local player may join a channel. It asks the server, so call it from a thread. + +#### ChangeChannel + +```lua +local changed = exports["zerio-radio"]:ChangeChannel(frequency) +``` + +Joins a channel and makes it the one the player talks on. Returns `false` for an invalid frequency or a dead player. The server still checks access, and shows the player an error when they don't have it. + + + To take the player off the radio, set their frequency to `0`. + + +#### GetCallsign + +```lua +local callsign = exports["zerio-radio"]:GetCallsign() +``` + +Returns the local player's callsign, or an empty string when they have none. + +#### SetCallsign + +```lua +exports["zerio-radio"]:SetCallsign(callsign) +``` + +Sets the local player's callsign and saves it. Players on the same channels see the new callsign straight away. + +#### GetSignalStrength + +```lua +local signal = exports["zerio-radio"]:GetSignalStrength() +``` + +Returns the local player's best signal across the channels they're on, from `0.0` to `1.0`. + +#### GetUnitBlips + +```lua +local blips = exports["zerio-radio"]:GetUnitBlips() +``` + +Returns the GPS blips zerio-radio shows the local player, as `{ [serverId] = blip }`. + +## Event List ### Server @@ -163,9 +591,7 @@ end) so a cheater could fake a panic or a channel join. -The player is always the first argument. Use it instead of the global `source`, which isn't set for events triggered by the server. - -Frequencies are numbers, such as `100` or `100.5`. +When an event is about a player, they're the first argument. Use it instead of the global `source`, which isn't set for events triggered by the server. #### playerJoinedChannel @@ -207,7 +633,7 @@ AddEventHandler("zerio-radio:server:playerStartedTransmitting", function(source, | `source` | `number` | The player's server id | | `frequency` | `number` | The channel they're talking on | -Triggered when a player starts talking on a channel. It's only triggered once the server has accepted the transmission, so a player blocked by no-talk-over, a dead battery or missing access doesn't trigger it. +Triggered when a player starts talking on a channel. It's only triggered once the server has accepted the transmission, so a player blocked by no-talk-over, Signal 100, a dead battery or missing access doesn't trigger it. #### playerStoppedTransmitting @@ -236,10 +662,33 @@ AddEventHandler("zerio-radio:server:panicButtonPressed", function(source, freque | `frequency` | `number` | The channel the player talks on, or `0` if they're on no channel | | `coords` | `vector3` | Where the player was when they pressed it | -Triggered when a player presses the panic button on their radio. Presses within `Config.PanicButtonCooldown` are ignored, and nothing is triggered when `Config.EnablePanicButton` is `false`. +Triggered when a player presses the panic button on their radio. Presses within `Config.PanicButtonCooldown` are ignored, and nothing is triggered when `Config.EnablePanicButton` is `false`. When players open the radio with an item, they need a radio open to press it. `coords` is read by the server, so the player can't fake it. +#### dispatchAlert + +```lua +AddEventHandler("zerio-radio:server:dispatchAlert", function(alert) + print(alert.type, alert.source, alert.coords) +end) +``` + +One event for every alert a dispatch script should know about: a panic button press, or a man-down alert once the countdown runs out. Use it to connect a dispatch script zerio-radio doesn't support out of the box. + +| Field | Type | Description | +| -------------- | --------------- | ---------------------------------------------------------------------------- | +| `type` | `string` | `"panic"` or `"mandown"` | +| `source` | `number` | The player's server id | +| `frequency` | `number` | The channel the alert is for, or `0` for a panic from a player on no channel | +| `channelLabel` | `string` or nil | The channel's label | +| `coords` | `vector3` | Where the player was, read by the server | +| `street` | `string` or nil | The street name, for panic alerts | +| `name` | `string` or nil | The player's name, `nil` when they're anonymous | +| `callsign` | `string` or nil | The player's callsign | + +The settings in `configs/features/dispatch.lua` decide which alerts also go to zerio-dispatch, ps-dispatch or lb-tablet. To send them to another script, edit `Functions.SendDispatchAlert` in `server/functions.lua` and `client/functions.lua`. + #### signalLost ```lua @@ -274,19 +723,40 @@ AddEventHandler("zerio-radio:server:signalRestored", function(source, frequency) Triggered when signal comes back on a channel after `signalLost`. If the player leaves the channel before it comes back, you get `playerLeftChannel` instead. +#### signal100Started + +```lua +AddEventHandler("zerio-radio:server:signal100Started", function(frequency, activatedBy) end) +``` + +| Argument | Type | Description | +| ------------- | -------- | ----------------------------------------------------- | +| `frequency` | `number` | The channel | +| `activatedBy` | `string` | The dispatcher, or the name given to `StartSignal100` | + +Triggered when Signal 100 starts on a channel, from the dispatch console or `StartSignal100`. When a dispatcher starts it, the event can arrive up to 5 seconds later. + +#### signal100Ended + +```lua +AddEventHandler("zerio-radio:server:signal100Ended", function(frequency) end) +``` + +Triggered when Signal 100 ends on a channel. + #### jammerPlaced ```lua AddEventHandler("zerio-radio:server:jammerPlaced", function(source, position, jammer) end) ``` -| Argument | Type | Description | -| ---------- | ---------------- | ------------------------------------------------------------------------------------------------ | -| `source` | `number` | The player who placed it, or `0` when it was placed from the console or the `PlaceJammer` export | -| `position` | `vector3` or nil | Where a ground jammer was placed. `nil` for a personal jammer, which follows the player | -| `jammer` | `table` | The jammer, see below | +| Argument | Type | Description | +| ---------- | ---------------- | ---------------------------------------------------------------------------------------------------- | +| `source` | `number` | The player who placed or carries it, or `0` for a ground jammer placed from the console or an export | +| `position` | `vector3` or nil | Where a ground jammer was placed. `nil` for a personal jammer, which follows the player | +| `jammer` | `table` | The jammer, see below | -Triggered when a jammer is placed or a personal jammer is turned on, by a player, an admin or the `PlaceJammer` export. Only triggered when `Config.Jammers.enabled` is `true`. +Triggered when a jammer is placed or a personal jammer is turned on, by a player, an admin or the `PlaceJammer` export. The `jammer` table contains: @@ -314,3 +784,44 @@ AddEventHandler("zerio-radio:server:jammerRemoved", function(id) end) | `id` | `string` | The jammer's id, the same as in `jammerPlaced` | Triggered when a jammer is removed, whether it was picked up, destroyed, turned off, removed by an admin or the `RemoveJammer` export, or its owner disconnected. + +#### repeaterPlaced + +```lua +AddEventHandler("zerio-radio:server:repeaterPlaced", function(source, position, repeater) end) +``` + +| Argument | Type | Description | +| ---------- | --------- | ----------------------------------------------------------------------------------------------------- | +| `source` | `number` | The player who placed it, or `0` when it was placed from the console or the `PlaceRepeater` export | +| `position` | `vector3` | Where it was placed | +| `repeater` | `table` | `id`, `repeaterTypeId`, `label`, `position`, `range`, `falloff`, `frequencies`, `prop` and `placedBy` | + +#### repeaterRemoved + +```lua +AddEventHandler("zerio-radio:server:repeaterRemoved", function(id) end) +``` + +Triggered when a repeater is removed, whether it was picked up, removed by an admin or the `RemoveRepeater` export, or its owner disconnected. `id` matches the one in `repeaterPlaced`. + +## Commands + +| Command | Who can use it | What it does | +| ------------------------------- | ------------------------------------------------------------ | --------------------------------------------------- | +| `/radio` | Everyone, when the radio opens by command | Opens the radio | +| `/radioscanner` | Everyone, when the scanner opens by command | Opens the scanner | +| `/changeradio ` | Everyone | Joins a channel. `0` leaves the radio | +| `/callsign ` | Everyone | Sets your callsign | +| `/radioadmin` | Admins | Opens the admin menu | +| `/placejammer [type]` | Ace `zerio.jammer` | Places a ground jammer | +| `/personaljammer [type]` | Ace `zerio.jammer` | Turns a personal jammer on or off | +| `/removejammer ` | Ace `zerio.jammer`, console | Removes a jammer | +| `/listjammers` | Ace `zerio.jammer`, console | Lists jammers in the server console | +| `/placerepeater [type]` | Ace `zerio.repeater` | Places a repeater | +| `/removerepeater ` | Ace `zerio.repeater`, console | Removes a repeater | +| `/listrepeaters` | Ace `zerio.repeater`, console | Lists repeaters in the server console | +| `/listtowers` | Admins, console | Lists towers and their health in the server console | +| `zerio:testdispatch [priority]` | Console, or admins with the ace `command.zerio:testdispatch` | Sends a test notification to the dispatch console | + +The command names for opening the radio, the scanner, changing channel, callsigns and the admin menu can be changed in the config. See the [Configuration Guide](/radio/configurationguide). diff --git a/pages/radio/faq.mdx b/pages/radio/faq.mdx index 05bc1cc..11ebfb5 100644 --- a/pages/radio/faq.mdx +++ b/pages/radio/faq.mdx @@ -1,165 +1,405 @@ -import { Tabs, Callout } from "nextra/components"; -import Image from "next/image"; +import { Callout, Tabs } from "nextra/components"; -# Frequently asked questions +# Frequently Asked Questions -A list of frequently asked questions can be seen below. +Common questions about zerio-radio v3. If your question isn't answered here, ask in our [community server](https://discord.zerio-scripts.com). -If this is empty and you have a question, please turn to the support in our [community server](https://discord.zerio-scripts.com) instead! +--- -## How to set-up the radio as an item +## Items and Opening the Radio - +### How do I set up the radio as an item? + +Item mode is the default. Check that `configs/opentype.lua` has: + +```lua +Config.OpenType.Value = { "item" } +``` + +Then add the `radio` item to your inventory. The full item lists are in [Add the Items](/radio/installationguide#add-the-items). + + - In most inventories, except for ox_inventory and other inventories that store their usage callbacks locally. You'd simply have to go to the `zerio-radio/shared/opentype.lua` file. And change `Shared.OpenType.Value` to `"item"`. + Add the item to `ox_inventory/data/items.lua` with a client export: + + ```lua + return { + ['radio'] = { + label = 'Radio', + weight = 1000, + stack = false, + allowArmed = true, + consume = 0, + client = { + export = 'zerio-radio.Open', + } + }, + } + ``` + + Keep `stack = false`, so each radio keeps its own channels and battery. You don't need a `client.remove` callback; zerio-radio notices dropped and removed radios by itself. Use ox_inventory 2.47.0 or newer. + - 1. Copy the following item info into `ox_inventory/data/items.lua` + Add the item to `qb-core/shared/items.lua` with `useable = true` and `unique = true`: ```lua - ['radio'] = { - label = 'Radio', - weight = 1000, - stack = false, - allowArmed = true, - consume = 0, - client = { - export = 'zerio-radio.Open', - remove = function(total) - if total < 1 then - TriggerEvent('zerio-radio:client:removedradio') - end - end - } - }, + QBShared.Items = { + radio = { + name = 'radio', + label = 'Radio', + weight = 1000, + type = 'item', + image = 'radio.png', + unique = true, + useable = true, + shouldClose = false, + description = 'A portable radio.' + }, + } + ``` + + zerio-radio registers the item as usable by itself. + + + + + Add the item to the `items` table: + + ```sql + INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES + ('radio', 'Radio', 1000, 0, 1); ``` - 2. Change `Shared.OpenType.Value` in `zerio-radio/shared/opentype.lua` to `"custom"`. + zerio-radio registers the item as usable by itself. -## How to set-up the radio scanner as an item +If you don't want an item at all, use a command or keybind instead: + +```lua +Config.OpenType.Value = { "command", "keybind" } +``` + +### How do I set up the radio scanner as an item? + +The scanner opens with `/radioscanner` by default. To make it an item, set the open type in `configs/features/scanner.lua` and add the `radio_scanner` item. + + + + Set the open type to `"custom"`, so the `/radioscanner` command goes away and only the item opens the scanner: + + ```lua + Config.Scanner.OpenType = "custom" + ``` + + Then add the item with a client export: + + ```lua + return { + ['radio_scanner'] = { + label = 'Radio Scanner', + weight = 1000, + stack = false, + close = true, + client = { + export = 'zerio-radio.OpenScanner', + } + }, + } + ``` + + - - In most inventories, except for ox_inventory and other inventories that store their usage callbacks locally. You'd simply have to go to the `zerio-radio/shared/scanner.lua` file. And change `Shared.Scanner.OpenType` to `"item"`. + Set the open type to `"item"`: + + ```lua + Config.Scanner.OpenType = "item" + ``` + + Then add `radio_scanner` to `qb-core/shared/items.lua` with `useable = true` (see [Add the Items](/radio/installationguide#add-the-items)). + - 1. Copy the following item info into `ox_inventory/data/items.lua` + Set the open type to `"item"`: ```lua - ['radioscanner'] = { - label = 'Radio Scanner', - weight = 1000, - stack = false, - allowArmed = true, - consume = 0, - client = { - export = 'zerio-radio.OpenScanner', - } - }, + Config.Scanner.OpenType = "item" ``` - 2. Change `Shared.Scanner.OpenType` in `zerio-radio/shared/scanner.lua` to `"custom"`. + Then add `radio_scanner` to the `items` table (see [Add the Items](/radio/installationguide#add-the-items)). -## I have issues with mic clicks. +The item name comes from `Config.Scanner.Item` (default `"radio_scanner"`). + +### How do codeplug radios work? + +A codeplug radio is an item that only works on a fixed list of channels, and is the only way onto those channels. Use it for department radios, for example a police radio. + +1. Add the item to `Config.OpenType.Items` in `configs/opentype.lua`: + + ```lua + Config.OpenType.Items = { + ["radio"] = {}, + + ["police_radio"] = { + label = "Police Radio", + codeplug = { + allowOtherChannels = false, + channels = { + { frequency = 911, label = "Police - Main" }, + { frequency = 921, label = "Police - Tactical" }, + }, + }, + }, + } + ``` -If you're using pma-voice and do not get "mic clicks" when using the radio, remove [this line](https://github.com/AvarianKnight/pma-voice/blob/main/client/init/main.lua#L232) from pma-voice. +2. Add `police_radio` to your inventory the same way as `radio`. For ox_inventory, give it `client = { export = 'zerio-radio.Open' }`. +3. Give the item to your police players. - - Please note: The line number for older versions of pma-voice is 220, and newer is 232. - The line you should be removing is the "if check" line in the "playMicClicks" function. +What happens then: - Image of the line to remove +- A player using the police radio can only join 911 and 921, unless you set `allowOtherChannels = true`. +- Only players using a police radio can join 911 and 921. The channels' normal access rules are ignored. +- A plain `radio` can't tune to 911 or 921. +- Anyone who uses a police radio gets access, whatever their job. Carrying it without using it gives nothing. + +You can also set a channel's access to **Codeplug Only** in `/radioadmin`. Then only codeplug radios that list that frequency can join it. + + + Codeplug frequencies replace the access rules of those channels. Don't put a + frequency in a codeplug that other groups use, like EMS or Fire. -## Players can cancel combat rolls by talking over the radio. +--- + +## Channels and Access + +### How do I create channels? + +Open `/radioadmin` and go to the Channels page. Create a channel with a frequency, a label and an access type. Channels are saved in the database. + +For a quick start, run `sql/!data.sql` once. It adds 911 Police Radio, 912 EMS Radio, 913 Fire Radio and 100 Dispatch, plus nine towers. + +In the channel editor you can also set: -This gets caused by zerio-radio trying to put away the weapon when talking over the radio, which you can disable by setting `Shared.RemoveWeaponOnTalkingOnRadio` to `false` in `zerio-radio/shared/main.lua`. +- **No-talk-over**: Only one person can transmit at a time. Others get "Channel busy". +- **Signal 100**: Lets a dispatcher in the web dispatch panel take over the channel. While it is active, players can't transmit. Scripts can start it too, with [`StartSignal100`](/radio/devdocs#startsignal100). +- **Man-Down** and **Unit GPS**: See [below](#why-dont-man-down-alerts-or-unit-gps-work). +- **Mic Click Sound**: The push-to-talk sound set for the channel. + +Locking a channel in the admin menu stops everyone from joining it. + +Channels can also be created from other scripts with exports. See [Developer Documentation](/radio/devdocs). + +### How do I restrict who can join a channel? + +Pick an access type for the channel in `/radioadmin` and add the allowed values. A player who matches any value can join. + +| Access type | Values | Needs | +| -------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------ | +| Open | None. Everyone can join. | | +| Jobs | Job names, like `police` | QBCore, Qbox or ESX | +| Gangs (QBCore) | QBCore gang names | QBCore or Qbox | +| Gangs (rcore_gangs) | Gang name or tag | rcore_gangs | +| Gangs (op-crime) | Organisation label or index | op-crime | +| ACE Permissions | Ace names, like `zerio.radio.police` | | +| Identifier Whitelist | Player identifiers like `license:...`, or the framework identifier (QBCore citizen ID, ESX identifier) | | +| Discord Roles | Discord role IDs | zdiscord or Badger_Discord_API | +| Codeplug Only | None. Only codeplug radios that list this frequency. | | + + + A frequency that has no channel in `/radioadmin` is open to everyone. To keep + a frequency private, create a channel for it. + -## How do I disable pma-voice's default animation? +### Who can open /radioadmin? -For zerio-radio to be able to play its animation's, you need to disable pma-voice's default radio animations, as they otherwise overwrite zerio-radio's animations. +| Framework | Who has access | +| --------------- | ------------------------------------------ | +| QBCore and Qbox | Players with the QBCore `admin` permission | +| ESX | Players in the `admin` or `god` group | +| Standalone | Players with the `zerio.admin` ace | -You can do this by adding the following snippet to your `server.cfg` file: +On standalone, add the ace in `server.cfg`: +```cfg +add_ace group.admin zerio.admin allow ``` -setr voice_enableRadioAnim 0 + +The ace name is `Config.Admin.acePermission` in `configs/admin.lua`. + +Players listed in `DispatchJobs` or `AcePermissions` in `configs/features/patches.lua` can also open `/radioadmin`, but they only see the Patches page. + +### Why don't man-down alerts or unit GPS work? + +Both are turned on per channel, and both are off by default: + +1. Open `/radioadmin` and edit the channel. +2. Turn on **Man-Down** and/or **Unit GPS**. +3. Save. + +Also check that the master switch is on: `Config.ManDown.Enabled` in `configs/features/mandown.lua` and `Config.GPS.Enabled` in `configs/features/gps.lua`. + +For man-down, check `Config.ManDown.Trigger`. With `"ragdoll"`, death systems that keep the player in a "downed" animation (like qb-ambulancejob) don't trigger it. Use `"either"` (the default) or `"dead"`. + +--- + +## Signal + +### Why do players have no signal? + +Go through these in order: + +1. **Towers enabled with no towers.** If `enabled` is `true` in `configs/features/towers.lua`, players only have signal near a tower. With no towers in the database and `defaultSignal = 0.0`, nobody has signal and push-to-talk shows "No signal — cannot transmit". Add towers in `/radioadmin`, run `sql/!data.sql`, set `enabled = false`, or raise `defaultSignal`. +2. **Out of range, or the tower doesn't serve the frequency.** Each tower has a range and a list of frequencies (`all` for every frequency). Use the Signal Map in `/radioadmin` to see coverage. +3. **Destroyed tower.** Shot-down towers give no signal until they are repaired or their `downtime` runs out. `/listtowers` prints each tower's health to the server console. +4. **Signal zone.** A zone set to 0% in `/radioadmin` (Signal Zones) removes signal inside it. +5. **Jammer.** A ground or personal jammer nearby. `/listjammers` prints active jammers to the server console. +6. **Underground.** Signal drops to 20% underground by default (`undergroundPenalty` in `configs/features/occlusion.lua`). +7. **Weather.** Thunder and blizzards lower signal (`configs/features/weather.lua`). +8. **Battery.** A dead battery blocks the radio, and with `SignalPenalty` on, a low battery weakens signal (`configs/features/battery.lua`). + +--- + +## Voice + +### Players can't hear each other on the radio + +1. **Check the server console** for a red box from `[zerio-radio:voice]`. It means the token is missing or wrong, or `Domain`/`ServerId` is wrong. See [Troubleshooting](/radio/installationguide#troubleshooting). +2. **Check the region.** `Domain` in `configs/voice.lua` must be the region your server was set up in. If you switched region in the portal, update `Domain`. +3. **Check the channel.** Both players must be on the same frequency, and the talker must transmit on their active channel. +4. **Check the microphone.** The radio records from the microphone Windows uses as the default recording device, which can be a different one from the device in FiveM's voice chat settings. Set the right microphone as the default in Windows sound settings, and make sure Windows allows desktop apps to use the microphone. +5. **Check signal.** With no signal, push-to-talk is blocked with "No signal". See [Why do players have no signal?](#why-do-players-have-no-signal) +6. **Check deafen.** The "Toggle Radio Deafen" keybind mutes all incoming radio audio until pressed again. + +Dead players can't transmit. They keep hearing the radio unless `Config.DisconnectOnDeath` is `true`. + +### Do I still need to edit pma-voice for mic clicks? + +No. v3 has its own push-to-talk sounds, set in `configs/audio/micclicks.lua`. You don't need to edit pma-voice. If you made the v2 edit, you can undo it. + +### Do I need to turn off pma-voice's radio? + +No. When zerio-radio starts, it sets `voice_enableRadios`, `voice_enableRadioUi` and `voice_enableRadioAnim` to `0`, which turns off pma-voice's radio, radio UI and radio animation. pma-voice keeps handling proximity voice. You can remove `setr voice_enableRadioAnim 0` from `server.cfg` if you added it for v2. + +--- + +## Dispatch + +### How do I send panic alerts to my dispatch script? + +zerio-dispatch, ps-dispatch and lb-tablet work out of the box. With `Script = "auto"` in `configs/features/dispatch.lua`, panic alerts go to whichever of them is running. Set `Jobs` to the jobs that should get the alert. See [Dispatch Scripts](/radio/configurationguide#dispatch-scripts-configsfeaturesdispatchlua). + +Man-down alerts can go there too: set `Alerts.mandown.Enabled = true`. + +For any other dispatch script, either: + +- Listen to the [`dispatchAlert`](/radio/devdocs#dispatchalert) event from your own resource. It has the player, channel, coordinates, street, name and callsign. +- Or edit `Functions.SendDispatchAlert` in `server/functions.lua` and `client/functions.lua`. Both files are open for editing. + +### Do panic alerts show up in the dispatch console? + +Yes. Every panic alert shows up in the web dispatch panel, with the player's callsign and channel. Man-down alerts show up for dispatchers monitoring the channel. + +--- + +## Weapons + +### How do I stop the radio from holstering weapons? + +Set this in `configs/main.lua`: + +```lua +Config.RemoveWeaponOnRadio = false ``` -## Disconnect player on item drop. +When `true` (the default), the weapon is put away when the radio opens and while push-to-talk is held, and given back afterwards. With ox_inventory, ox_inventory's own disarm is used. - - The following does **not** apply to ox_inventory. This is automatically - handled if you use the snippet above. +If a player is in the middle of a combat roll when they press push-to-talk, the radio waits for the roll to finish before playing the talk animation. + +### How do I stop players shooting while they talk? + +Set this in `configs/gameplay.lua`: + +```lua +Config.DisableShootingWhileTalkingOnRadio = true +``` + + + Holstering on push-to-talk and `DisableShootingWhileTalkingOnRadio` only work + while a talk animation plays. If you remove `TalkAnimation` from + `configs/features/animation.lua` (and no clothing rule matches), neither + happens. - - - Open the `es_extended/server/classes/player.lua` file. Then find the function named `removeInventoryItem`. +--- - We want to add the following snippet after the line triggering `esx:removeInventoryItem`. +## Animations & Props - ```lua - if tostring(itemName) == "radio" and newCount == 0 then - TriggerClientEvent("zerio-radio:client:removedradio", self.source) - end - ``` +All of these are in `configs/features/animation.lua`. Bone IDs are listed on the [RAGE:MP wiki](https://wiki.rage.mp/index.php?title=Bones); use the `ID` value. - +### Change or disable the talking animation - - Open the `qb-inventory/server/functions.lua` file. Then find the function named `RemoveItem`. +Change `Dictionary` and `Name` under `TalkAnimation`. `StopSpeed` (1.0 to 8.0, keep the `.0`) sets how fast the animation stops; 8.0 is instant. - We want to add the following code snippet, before each `return true` of the function. Depending on how new/old your files are, there may be only one, or multiple true returns. +To disable it, remove the `TalkAnimation` block or comment it out. - ```lua - if tostring(item) == "radio" then - TriggerClientEvent("zerio-radio:client:removedradio", Player.PlayerData.source) - end - ``` +### Change or disable the open animation - - +`OpenAnimation` has two animations: `Normal` (on foot) and `InCar` (in a vehicle). Change `Dictionary` and `Name` in either. -## Animations & Props +To disable both, remove the `OpenAnimation` block. To disable only one, remove `Normal` or `InCar`. + +### Change or disable the props -### Change/disable talking animation +`OpenProp` is the prop held while the radio is open. `TalkProp` is the prop held while talking. In each: -You can disable the talking animation by removing/commenting out the `TalkAnimation` value in `zerio-radio/shared/animation.lua`. +- `Model`: The prop model. The default `zerio_radio` comes from `zerio-radio-prop`. +- `Bone`: The ped bone ID the prop is attached to. +- `Offset` and `Rotation`: The prop's position and rotation on the bone. -Changing the animation would be done via the sub values of `TalkAnimation`. +To disable a prop, remove its block or comment it out. -### Change/disable open animation +### Use a different talking animation based on clothing -Changing the animation played upon opening the radio can be done via the `OpenAnimation` subvalues. +`ClothingAnimations` is a list of rules. The first rule whose components all match is used instead of `TalkAnimation`. For example, a hand-to-ear animation for players wearing certain masks: -Disabling the animation can be done via removing/commenting out the `OpenAnimation` value. +```lua +ClothingAnimations = { + { + Components = { + { id = 1, drawables = { 45, 46, 49 } }, + }, + Animation = { + Dictionary = "random@arrests@busted", + Name = "idle_a", + StopSpeed = 1.0, + }, + }, +} +``` -### Change/disable talking prop +- `id`: The clothing component, for example `1` (mask), `3` (torso), `9` (body armor) or `11` (top). Props use negative IDs: `-1` hat, `-2` glasses, `-3` ear. +- `drawables`: The drawable IDs that match, or a range like `{ min = 50, max = 60 }`. Always list drawables. Without them, the rule matches every player. -Changing the talking prop can be done via the `TalkProp` subvalues. +--- - - List of player bones can be found - [here](https://wiki.rage.mp/index.php?title=Bones). You'd use the `ID` value. - +## Customization -Disabling the talking prop can be done by removing/commenting out the `TalkProp` value. +### How do I change the notifications? -### Change/disable open prop +Notifications use ox_lib. To use another notification system, edit `Notify` in `client/functions.lua` and `Notify` in `server/functions.lua`. Both files are open for editing. -Changing the prop spawned when opening the radio can be done via the `OpenProp`. +### How do I translate the radio? - - List of player bones can be found - [here](https://wiki.rage.mp/index.php?title=Bones). You'd use the `ID` value. - +Copy `locales/en.json` to a new file, for example `locales/de.json`, translate it, and set `Config.Locale = "de"` in `configs/main.lua`. This translates notifications and command help. -Disabling the talking prop can be done by removing/commenting out the `OpenProp` value +The text inside the radio itself is built into the radio's interface, which has English and Swedish. For other languages it shows English. Contact support if you want your translation added to the interface. See [Translations](/radio/configurationguide#translations-locales). diff --git a/pages/radio/installationguide.mdx b/pages/radio/installationguide.mdx index 104ef3f..e1c5bed 100644 --- a/pages/radio/installationguide.mdx +++ b/pages/radio/installationguide.mdx @@ -1,7 +1,536 @@ +import { Callout, Steps, Tabs } from "nextra/components"; + # Installation Guide -Assuming you have all dependencies noted, there should be no specific installation steps required. You would simply add the script to your server and start it. +This guide walks you through installing zerio-radio v3 on your FiveM server. If you are coming from v2, read [Upgrading from v2](#upgrading-from-v2) first. + +## Prerequisites + +Before installing, make sure you have: + +- OneSync enabled +- [oxmysql](https://github.com/overextended/oxmysql) and [ox_lib](https://github.com/overextended/ox_lib) installed and started +- QBCore, Qbox or ESX, or no framework (standalone) +- If you use ox_inventory: version 2.47.0 or newer +- The setup link from your purchase email (you need it to set up the voice server) + + + Stop any other radio resource (for example qb-radio, or an old copy of + zerio-radio) before you install. Both would react to the same `radio` item. + + +## Installation Steps + + + +### Download and Extract + +Download zerio-radio and extract it. The download contains two resources: `zerio-radio` and `zerio-radio-prop`. `zerio-radio-prop` comes inside the `zerio-radio` folder. Move it out so both sit next to each other in your resources folder: + +``` +resources/ + └── [zerio]/ + ├── zerio-radio/ + └── zerio-radio-prop/ +``` + + + `zerio-radio-prop` must be its own resource. FiveM does not load a resource + that is nested inside another resource's folder, and zerio-radio will not + start without it. + + +### Set Up the Voice Server + +All radio audio goes through Zerio's hosted voice server. You set it up once in the customer portal: + +1. Open the setup link from your purchase email. +2. Pick the region closest to where your FiveM server is hosted (EU or NA). +3. Enter a Server ID, for example `my-rp-server` (letters, numbers, hyphens and underscores). +4. Optionally set a portal email. You need it to sign in to the portal later. +5. Copy the values the wizard shows you. **The admin token is only shown once**, so save it somewhere safe. + +Open `configs/voice.lua` and fill in `Domain` and `ServerId` from the wizard, and set `PortalUrl` to the hosted portal: + +```lua +Config.VoiceServer = { + Enabled = true, + Domain = "eu.radio.zerio-scripts.com", -- or "na.radio.zerio-scripts.com" + ServerId = "my-rp-server", + PortalUrl = "https://portal.radio.zerio-scripts.com", +} +``` + +Then add the admin token to your `server.cfg`: + +```cfg +set zerio_voice_admin_token "your-token-from-the-setup-wizard" +``` + + + Use `set`, never `setr`. `setr` sends the value to every player's game, and + the token gives full control over your voice server. + + +To manage your server later, sign in at [portal.radio.zerio-scripts.com](https://portal.radio.zerio-scripts.com) with your Server ID and portal email. If you switch region there, update `Domain` in `configs/voice.lua` to match. + +### Add the Items + +By default players open the radio by using a `radio` item. Add the items to your inventory. If you run standalone with no inventory, skip to the Standalone tab. + +Item images are in the `_ITEM IMAGES` folder of the download: `radio`, `radio_battery`, `radio_scanner`, `radio_jammer`, `mobile_radio_jammer` and `radio_repeater`. There is no image for `radio_repair_kit`, so add your own if you want one. Copy the images to your inventory's image folder (`ox_inventory/web/images/` or `qb-inventory/html/images/`). + + + + Use this tab for ox_inventory on any framework, including Qbox. Add the entries to `ox_inventory/data/items.lua`: + + ```lua + return { + -- your existing items ... + + ['radio'] = { + label = 'Radio', + weight = 1000, + stack = false, + allowArmed = true, + consume = 0, + client = { + export = 'zerio-radio.Open', + } + }, + + ['radio_scanner'] = { + label = 'Radio Scanner', + weight = 1000, + stack = false, + close = true, + }, + + ['radio_jammer'] = { + label = 'Radio Jammer', + weight = 1000, + stack = false, + close = true, + }, + + ['mobile_radio_jammer'] = { + label = 'Mobile Radio Jammer', + weight = 1000, + stack = false, + close = true, + }, + + ['radio_repeater'] = { + label = 'Radio Repeater', + weight = 1000, + stack = false, + close = true, + }, + + ['radio_repair_kit'] = { + label = 'Radio Repair Kit', + weight = 500, + stack = true, + close = true, + }, + } + ``` + + The radio must have `stack = false`. Each radio keeps its own connected channels and battery charge on the item. + + Every codeplug radio you add in `configs/opentype.lua` (for example `police_radio`) needs an entry like `radio`, with the same `client.export = 'zerio-radio.Open'`. + + If you turn on the [battery system](/radio/configurationguide#battery-configsfeaturesbatterylua), also add the battery item: + + ```lua + return { + ['radio_battery'] = { + label = 'Radio Battery', + weight = 100, + stack = false, + close = true, + consume = 0, + server = { + export = 'zerio-radio.UseBatteryItem', + } + }, + } + ``` + + The jammer and repeater items are registered as usable items through your framework, which ox_inventory passes on. On a standalone server without a framework, use the commands instead (see the Standalone tab). + + + You do not need a `client.remove` callback on the radio. zerio-radio + notices when a radio is dropped, given away, stored or removed, and + disconnects that radio's channels. + + + + + + Add the entries to `qb-core/shared/items.lua`: + + ```lua + QBShared.Items = { + -- your existing items ... + + radio = { + name = 'radio', + label = 'Radio', + weight = 1000, + type = 'item', + image = 'radio.png', + unique = true, + useable = true, + shouldClose = false, + description = 'A portable radio.' + }, + + radio_battery = { + name = 'radio_battery', + label = 'Radio Battery', + weight = 100, + type = 'item', + image = 'radio_battery.png', + unique = true, + useable = true, + shouldClose = true, + description = 'Replacement battery for the radio.' + }, + + radio_scanner = { + name = 'radio_scanner', + label = 'Radio Scanner', + weight = 1000, + type = 'item', + image = 'radio_scanner.png', + unique = false, + useable = true, + shouldClose = true, + description = 'Picks up nearby radio transmissions.' + }, + + radio_jammer = { + name = 'radio_jammer', + label = 'Radio Jammer', + weight = 1000, + type = 'item', + image = 'radio_jammer.png', + unique = false, + useable = true, + shouldClose = true, + description = 'Disrupts radio signals in the area.' + }, + + mobile_radio_jammer = { + name = 'mobile_radio_jammer', + label = 'Mobile Radio Jammer', + weight = 1000, + type = 'item', + image = 'mobile_radio_jammer.png', + unique = false, + useable = true, + shouldClose = true, + description = 'Portable radio jammer.' + }, + + radio_repeater = { + name = 'radio_repeater', + label = 'Radio Repeater', + weight = 1000, + type = 'item', + image = 'radio_repeater.png', + unique = false, + useable = true, + shouldClose = true, + description = 'Boosts radio signal coverage in an area.' + }, + + radio_repair_kit = { + name = 'radio_repair_kit', + label = 'Radio Repair Kit', + weight = 500, + type = 'item', + image = 'radio_repair_kit.png', + unique = false, + useable = false, + shouldClose = true, + description = 'Tools and parts to repair a radio tower.' + }, + } + ``` + + `unique = true` is needed on the radio and the battery so each one keeps its own battery charge. The scanner item only opens the scanner if you set `Config.Scanner.OpenType = "item"`. Codeplug radios (for example `police_radio`) need an entry like `radio`. + + Qbox servers use ox_inventory, so use the ox_inventory tab instead. + + + + + For ESX's built-in inventory, add the items to the `items` table: + + ```sql + INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES + ('radio', 'Radio', 1000, 0, 1), + ('radio_battery', 'Radio Battery', 100, 0, 1), + ('radio_scanner', 'Radio Scanner', 1000, 0, 1), + ('radio_jammer', 'Radio Jammer', 1000, 0, 1), + ('mobile_radio_jammer', 'Mobile Radio Jammer', 1000, 0, 1), + ('radio_repeater', 'Radio Repeater', 1000, 0, 1), + ('radio_repair_kit', 'Radio Repair Kit', 500, 0, 1); + ``` + + ESX's built-in items can't store data per item, so battery charge is kept in server memory for the session and is not shown on the item. If you use ox_inventory with ESX, use the ox_inventory tab instead. + + + + + Standalone servers have no items. Open `configs/opentype.lua` and change how players open the radio, because the default (`"item"`) needs an inventory: + + ```lua + Config.OpenType.Value = { "command", "keybind" } + ``` + + Players then use `/radio` or press F5. Jammers and repeaters are placed with commands that need an ace permission. Admin menu access also uses an ace. Add the ones you need to `server.cfg`: + + ```cfg + add_ace group.admin zerio.admin allow + add_ace group.admin zerio.jammer allow + add_ace group.admin zerio.repeater allow + ``` + + + + +### Add to server.cfg + +Start zerio-radio after its dependencies. The order is: oxmysql, ox_lib, your framework, your inventory, optional resources (target, Discord, dispatch), `zerio-radio-prop`, then `zerio-radio`. + +```cfg +# Voice server token (set, not setr) +set zerio_voice_admin_token "your-token-from-the-setup-wizard" + +# Dependencies +ensure oxmysql +ensure ox_lib + +# Framework and inventory +ensure qb-core +ensure ox_inventory + +# Optional +ensure ox_target + +# Zerio Radio +ensure zerio-radio-prop +ensure zerio-radio +``` + +### Start Your Server + +Start or restart your server. zerio-radio creates its database tables on the first start, so you don't need to run any SQL by hand. + +When the voice server is set up correctly, the console shows a line like this and no red warning box: + +``` +[zerio-radio:voice] loaded — voice server at https://eu.radio.zerio-scripts.com/s/my-rp-server +``` + +If you see a red box instead, check [Troubleshooting](#troubleshooting). + +### Create Channels and Towers + +Channels and towers live in the database. Join the server and open the admin menu with `/radioadmin` to create them. See [Who can open /radioadmin?](/radio/faq#who-can-open-radioadmin) if you get no access. + +If you want a starting point, run `sql/!data.sql` once on your database. It adds four channels (911 Police Radio, 912 EMS Radio, 913 Fire Radio and 100 Dispatch) and nine towers around the map. Running it a second time fails, because those frequencies and tower IDs already exist. + + + When towers are enabled (`enabled` in `configs/features/towers.lua`), players + only have signal near a tower. With towers enabled and no towers in the + database, nobody has signal and push-to-talk is blocked with "No signal". Add + towers in `/radioadmin` (or run `sql/!data.sql`), turn towers off, or raise + `defaultSignal`. See [Radio + Towers](/radio/configurationguide#radio-towers-configsfeaturestowerslua). + + +### Test the Installation + +1. Give yourself a `radio` item and use it (or use `/radio` or F5 if you changed the open type). +2. Join a channel, for example 911. +3. Have a second player join the same channel. +4. Hold Left Alt and talk. The other player should hear you. + + + +## Commands and Keybinds + +### Commands + +| Command | Who | Description | +| -------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `/radio` | Everyone | Opens the radio. Only exists when `Config.OpenType.Value` contains `"command"`. | +| `/radioscanner` | Everyone | Opens the radio scanner. Only exists when `Config.Scanner.OpenType` is `"command"` (the default). | +| `/changeradio ` | Everyone | Joins a frequency without opening the radio, or leaves the radio with `0`. In item mode the player must carry a radio and have used it. | +| `/callsign ` | Everyone | Sets your callsign. Run it with no text to clear it. | +| `/radioadmin` | Admins | Opens the admin menu. | +| `/placejammer [type]` | Ace `zerio.jammer` | Starts placing a ground jammer. | +| `/personaljammer [type]` | Ace `zerio.jammer` | Turns your personal jammer on or off. | +| `/removejammer ` | Ace `zerio.jammer` | Removes a jammer. | +| `/listjammers` | Ace `zerio.jammer` | Prints active jammers and their IDs to the server console. | +| `/placerepeater [type]` | Ace `zerio.repeater` | Starts placing a repeater. | +| `/removerepeater ` | Ace `zerio.repeater` | Removes a repeater. | +| `/listrepeaters` | Ace `zerio.repeater` | Prints active repeaters and their IDs to the server console. | +| `/listtowers` | Admins | Prints every tower and its health to the server console. | +| `zerio:testdispatch` | Server console, or admins with the ace `command.zerio:testdispatch` | Sends a test alert to the dispatch panel. Optional priority: `low`, `medium`, `high` or `critical`. | + +The command names for `/radio`, `/radioscanner`, `/changeradio`, `/callsign` and `/radioadmin` can be changed in the config. The jammer and repeater commands work on every framework, not only standalone. + +### Keybinds + +Keybinds are registered through ox_lib. Players can change them in **Settings → Key Bindings → FiveM**. + +| Keybind | Default key | Notes | +| --------------------------------------- | ----------- | ----------------------------------------------------------- | +| Talk over Radio | Left Alt | Push-to-talk on your active channel. | +| Open Radio | F5 | Only when `Config.OpenType.Value` contains `"keybind"`. | +| Open Radio Scanner | F6 | Only when `Config.Scanner.OpenType` is `"keybind"`. | +| Toggle Radio Deafen | Not bound | Mutes all incoming radio audio without leaving any channel. | +| Cancel Man-Down Alert | Backspace | Cancels a man-down countdown that started by mistake. | +| Confirm Speaker Placement (Zerio Radio) | Enter | Used by admins when placing a speaker. | +| Cancel Speaker Placement (Zerio Radio) | Backspace | Used by admins when placing a speaker. | + +## Upgrading from v2 + +v3 is a new install, not an update you copy over v2. The configuration moved from `shared/*.lua` (`Shared.*`) to the `configs/` folder (`Config.*`), and many options changed. + +### Upgrade Steps + +1. Back up your database and your v2 config files. +2. Delete the old `zerio-radio` folder and install v3 by following the steps above. Don't copy v2 files into v3. +3. Before you start v3 for the first time, rename the two v2 tables that changed. v3 does not convert them: + + ```sql + RENAME TABLE zerio_radio_settings TO zerio_radio_settings_v2; + RENAME TABLE zerio_radio_messages TO zerio_radio_messages_v2; + ``` + + v3 then creates new tables. Players' v2 radio settings (position, size, callsign and so on) and message history are not carried over. Contacts, favorites and recent channels keep working. + +4. Re-create your channels in `/radioadmin`. `Shared.Channels` is gone; channels are stored in the database. +5. Undo the v2 inventory edits. v3 detects removed radios by itself: + - ox_inventory: remove the `client.remove` callback from the `radio` item. + - ESX: remove the `zerio-radio:client:removedradio` snippet from `removeInventoryItem` in `es_extended/server/classes/player.lua`. + - QBCore: remove the `zerio-radio:client:removedradio` snippet from `RemoveItem` in `qb-inventory/server/functions.lua`. +6. The scanner item is now called `radio_scanner` (it was `radioscanner`). Rename the item in your inventory, or set `Config.Scanner.Item = "radioscanner"`. +7. You no longer need `setr voice_enableRadioAnim 0` in `server.cfg` or the pma-voice mic click edit. zerio-radio turns pma-voice's radio off by itself and plays its own push-to-talk sounds. + +### Where v2 Options Went + +| v2 option | v3 | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `Shared.Locale`, `ShowPlayerList`, `DisableAnonymous`, `ChangeRadioChannelCommand`, `MessageLimit`, `RecentLimit`, `AllowCallSigns`, `CallsignCommand`, `DisplayRadioName` | Same names under `Config.` in `configs/main.lua` | +| `Shared.RemoveWeaponOnOpenRadio`, `Shared.RemoveWeaponOnTalkingOnRadio` | One option: `Config.RemoveWeaponOnRadio` in `configs/main.lua` | +| `Shared.DisconnectOnDeath`, `DisconnectInWater`, `MinimumWaterDepth`, `RemoveOnOffDuty`, `DisableShootingWhileTalkingOnRadio` | Same names under `Config.` in `configs/gameplay.lua` | +| `Shared.EnablePanicButton`, `PanicButtonCooldown` | `configs/features/panic.lua` | +| `Shared.PanicButtonSound` | Removed. The radio plays `html/sounds/panic_alert.ogg`; no sound resource needed. | +| `Shared.RadioFilter` | Removed. Radio audio filtering is set in `configs/audio/submix.lua`. | +| `Shared.RadioKey` | Removed. Push-to-talk is an ox_lib keybind players can rebind. | +| `Shared.NavigateWithArrowKeys` | Removed. | +| `Shared.OpenType.*` | `Config.OpenType.*` in `configs/opentype.lua`. `Item` became the `Items` table. The `"control"` type and `Button` are removed. | +| `Shared.Scanner.*` | `Config.Scanner.*` in `configs/features/scanner.lua`. The `"control"` type and `Button` are removed. | +| `Shared.Animation` | `Config.Animation` in `configs/features/animation.lua` | +| `Shared.Channels` | Channels in the database, managed in `/radioadmin` | +| `locales/*.lua` | `locales/*.json` | + +### Changed Defaults + +| Option | v2 | v3 | +| ------------------------------- | ------------- | ----------- | +| `OpenType.Value` | `"keybind"` | `"item"` | +| Scanner `OpenType` | `"item"` | `"command"` | +| Scanner `Distance` | `100` | `500` | +| `PanicButtonCooldown` | `300` seconds | `5` seconds | +| `DisconnectOnDeath` | `true` | `false` | +| `DisconnectInWater` | `true` | `false` | +| Weapon removal on open and talk | `false` | `true` | + +## Troubleshooting + +### Red "admin token missing" box in the console + +**Cause**: The `zerio_voice_admin_token` convar is not set, is empty, or is still `CHANGE_ME`. + +**Solution**: + +- Add `set zerio_voice_admin_token ""` to `server.cfg`, above `ensure zerio-radio`. +- Restart the server. Radio voice and dispatch stay off until the token is set. +- If you lost the token, sign in at [portal.radio.zerio-scripts.com](https://portal.radio.zerio-scripts.com) with your Server ID and portal email, and use **Reset admin token** in the account settings. Then put the new token in `server.cfg`. + +### Red "admin token REJECTED" box in the console + +**Cause**: The voice server does not accept the token. + +**Solution**: + +- Check that you copied the whole token, inside the quotes. +- Check that you used `set`, not `setr`. +- Check that `Domain` and `ServerId` in `configs/voice.lua` belong to the same server the token was made for. +- Restart zerio-radio after changing `server.cfg`. + +### Red "voice admin API unreachable" box in the console + +**Cause**: zerio-radio can't reach the voice server at the address built from `configs/voice.lua`. + +**Solution**: + +- Check `Domain`: only the host, like `eu.radio.zerio-scripts.com`, with no `https://` and no trailing slash. +- Check `ServerId`: it must match the Server ID you entered in the setup wizard. +- Check that your host's firewall allows outgoing HTTPS connections. + +### "Could not find dependency zerio-radio-prop" + +**Cause**: `zerio-radio-prop` is missing, still inside the `zerio-radio` folder, or not started. + +**Solution**: + +- Move `zerio-radio-prop` out so it sits next to `zerio-radio`. +- Add `ensure zerio-radio-prop` above `ensure zerio-radio`. + +### "No signal — cannot transmit" + +**Cause**: The player's signal on the active channel is zero. Most often towers are enabled but there are no towers in the database. A jammer, a 0% signal zone or a dead battery also cause it. + +**Solution**: See [Why do players have no signal?](/radio/faq#why-do-players-have-no-signal) + +### Using the radio item does nothing + +**Cause**: The open type or the item definition doesn't match your setup. + +**Solution**: + +- Check that `Config.OpenType.Value` in `configs/opentype.lua` is `{ "item" }`. +- ox_inventory: check that the item has `client = { export = 'zerio-radio.Open' }`. +- QBCore: check that the item has `useable = true`. +- Check that the item name is a key in `Config.OpenType.Items`. + +### Players can't hear each other + +**Cause**: Voice is not connected, or the player's microphone is not the one the radio uses. + +**Solution**: See [Players can't hear each other on the radio](/radio/faq#players-cant-hear-each-other-on-the-radio). + +### "ox_inventory ... has no post-hook events" warning + +**Cause**: Your ox_inventory is older than 2.47.0. + +**Solution**: Update ox_inventory. On older versions every copy of a radio item acts as one radio, and drops and gives are noticed late. + +### Database errors after upgrading from v2 + +**Cause**: The v2 `zerio_radio_settings` or `zerio_radio_messages` table is still in the database. You see errors like "Unknown column" or "Field 'timestamp' doesn't have a default value". + +**Solution**: Rename the old tables as shown in [Upgrade Steps](#upgrade-steps), then restart the server. -## Configure the resource +## Need Help? -We do **highly** recommend to familiarize yourself with, or atleast look through, the configuration files. These files are in the `shared` folder of Zerio Radio. +If you're still having issues, join our [community server](https://discord.zerio-scripts.com) and create a support ticket. diff --git a/public/radio_mic_clicks_img.png b/public/radio_mic_clicks_img.png deleted file mode 100644 index 249903b..0000000 Binary files a/public/radio_mic_clicks_img.png and /dev/null differ