Radio V3

A modern handheld radio for pma-voice with everything your server needs for law enforcement, medic and civilian radio comms.

Table of Contents

  1. Features
  2. Requirements
  3. Installation
  4. Configuration
  5. Data Files
  6. Customising
  7. Keybinds & Commands
  8. Exports
  9. Locales
  10. Support

Features

Radio & UI

  • Touchscreen radio with apps for tuning, channels, favorites, listening in, the player list and settings.
  • Custom radio props, animations and item images included.
  • Radio prop held in hand while the radio is open, with its screen also shown on the props screen.
  • Movable and resizable radio and radio player list that saves.
  • 18 preset colors, custom color picker, 4 finishes and 24 designs.
  • Add-on design pack support.
  • Selectable talk animations, with support for adding custom animations.
  • Radio notifications that pop up on the radio UI.

Channels

  • Channels from 1 to 999.99 with ability to add custom labels.
  • Channels app with a list of configurable categories and channels.
  • Channel locking by state bag, job/gang grade or ACE permissions, with an optional on duty check.
  • Add up to 12 favorite channels.
  • Automatic rejoin of the last channel when you power the radio on. (even after a server restart)
  • You can listen in on up to 4 other channels, basically meaning you can be in up to 5 channels at once. You can also adjust this.
  • Radio player list showing names, callsigns and who is talking on your channel and the channels you're listening in on.
  • Editable names and callsigns (can be disabled in config, recommended for framework users), and muting of other players.
  • Optional talkover protection with a busy tone.
  • Optional transmission timeout to limit how long people can speak.

Emergency

  • Panic button on configurable emergency channels.
  • Alerts with a sound, radio notification and flashing map blip for everyone on the channel and anyone listening in.
  • Configurable cooldown and blip duration.

Signal

  • Voice quality based on distance.
  • Worse signal in interiors, tunnels, under water, in bad weather and in configurable areas.
  • Better signal when high up or in a vehicle.
  • Optional, can be disabled for perfect audio everywhere.

Jammers

  • Placeable signal jammers that block every channel except the ones whoever placed it allow.
  • Item or command placement, with a green or red preview for valid and invalid spots.
  • Jammer control screen with power, range and allowed channels.
  • Pick up jammers with G, or you can also enable ox_target instead. (Support for your own custom interactions via export too)
  • Destructible jammers with bullet impacts and sparks.
  • Permanent jammers set in the config.

Battery

  • Battery drains while powered on, drains faster while talking.
  • Sitting in a vehicle charges your radio.
  • Battery saved per radio item, or per player if you don't use the item system.
  • Power bank items that charge radios over time and recharge in vehicles.
  • Optional, can be disabled.

Items & Frameworks

  • Optional radio item, with optional items for every color and design.
  • ox_inventory and qb-inventory support.
  • QBCore, Qbox, ESX and standalone support.

Exclusive Law Enforcement App

  • Exclusive to scully_lawenforcement (opens in a new tab) customers.
  • Duty toggle and on duty list.
  • 10-code statuses shown on the radio player list.
  • Automatic wellness check alerts for officers that haven't moved for a while.

Other

  • Rebindable keybinds.
  • Exports and events.
  • Locale support.

Requirements

The resource manifest declares:

dependencies {
    '/server:35245', -- Server artifacts / build number
    '/onesync',
    'pma-voice'
}

Optional:

  • QBCore, Qbox or ESX, for jobs and character names.
  • ox_inventory or qb-inventory, if you want the radio to be an item.
  • scully_lawenforcement, for the police app.

Installation

  1. Download the resource from your FiveM Asset Grants (opens in a new tab).

  2. Extract scully_radio3 into your server's resources folder.

  3. Move scully_radio3.cfg out of the resource and place it next to your server.cfg.

  4. Add this to the top of your server.cfg:

    exec scully_radio3.cfg
  5. Start the resource:

    ensure pma-voice
    ensure scully_radio3
  6. If you want the radio to be an item, add the items to your inventory (see Items).

  7. Restart your server.


Permissions

Private channels and jammers can be locked by state bag, job, gang or ace permission. If you use aces, add them to your server.cfg:

add_ace group.police group.police allow
add_ace group.ems group.ems allow
add_ace group.police group.jammer allow

Configuration

Server-wide settings live in scully_radio3.cfg.

Core

setr scully_radio3:uniqueKvpId "change-me"
setr scully_radio3:locale "en"
SettingDescription
uniqueKvpIdSomething unique such as your server name, it's used to save each player's radio settings.
localeThe language of the radio, from the locales folder.

Controls

setr scully_radio3:openCommand "radio"
setr scully_radio3:openKey "U"
setr scully_radio3:nextChannelKey "PAGEUP"
setr scully_radio3:previousChannelKey "PAGEDOWN"
setr scully_radio3:swapChannelKey ""
setr scully_radio3:scanKey ""
setr scully_radio3:powerKey ""
setr scully_radio3:volumeUpKey ""
setr scully_radio3:volumeDownKey ""
SettingDescription
openCommandCommand to open the radio, set to "" to turn it off.
openKeyKey to open the radio. (key names (opens in a new tab))
nextChannelKeyGoes to the next favorite channel, or the next channel if the player has no favorites.
previousChannelKeyGoes to the previous favorite channel, or the previous channel if the player has no favorites.
swapChannelKeyJumps back to the last channel the player was on.
scanKeyTurns listening to extra channels on and off.
powerKeyTurns the radio on and off.
volumeUpKeyTurns the volume up.
volumeDownKeyTurns the volume down.

Leave a key empty to let players pick their own in their GTA key settings.

Items & looks

setr scully_radio3:useItem "false"
setr scully_radio3:itemColors "false"
setr scully_radio3:allowColors "true"
setr scully_radio3:allowCustomColors "true"
setr scully_radio3:enablePrideRadios "true"
setr scully_radio3:defaultColor "graphite"
setr scully_radio3:enableEditing "true"
SettingDescription
useItemSet to "true" so players need a radio item to use the radio.
itemColorsSet to "true" to have a separate item for every color and design (radio_blue, radio_woodland and so on). The radio is then always the color of the item.
allowColorsSet to "false" to stop players changing their radio's color.
allowCustomColorsSet to "false" to only allow the preset colors.
enablePrideRadiosSet to "false" to remove the pride radios.
defaultColorThe color new players start with, a preset name from data/colors.lua or a hex color like #1f5fd1.
enableEditingSet to "false" to stop players changing the name and callsign others see on the radio.

Channels

setr scully_radio3:minFrequency 1
setr scully_radio3:maxFrequency 999.99
setr scully_radio3:maxFavorites 12
setr scully_radio3:maxScanChannels 4
setr scully_radio3:rememberChannel "true"
setr scully_radio3:talkoverProtection "true"
setr scully_radio3:transmitTimeout 60
SettingDescription
minFrequencyThe lowest channel players can join.
maxFrequencyThe highest channel players can join.
maxFavoritesHow many favorite channels each player can save.
maxScanChannelsHow many other channels a player can listen to at the same time, set to 0 to turn it off.
rememberChannelSet to "false" if the radio shouldn't go back to the last channel when it's turned on.
talkoverProtectionStops players talking over someone who is already talking, they hear a busy tone instead. You can turn this on or off for each group of channels in data/channels.lua.
transmitTimeoutCuts players off after talking for this many seconds, so a stuck key doesn't block the channel. Set to 0 to turn it off.

Panic button

setr scully_radio3:enableEmergency "true"
setr scully_radio3:emergencyCooldown 30
setr scully_radio3:emergencyBlipTime 60
SettingDescription
enableEmergencySet to "false" to remove the panic button. It only works on channels you allow it on in data/channels.lua.
emergencyCooldownHow many seconds a player has to wait before pressing it again.
emergencyBlipTimeHow many seconds the blip stays on the map.

Signal & jammers

setr scully_radio3:enableSignal "true"
setr scully_radio3:enableJammers "true"
SettingDescription
enableSignalSet to "false" to make everyone sound perfect no matter where they are. Tweak how signal works in data/signal.lua.
enableJammersSet to "false" to remove jammers. Set them up in data/jammers.lua.

Battery

setr scully_radio3:enableBattery "true"
setr scully_radio3:batteryLife 120
setr scully_radio3:transmitDrain 3
setr scully_radio3:vehicleCharging "true"
setr scully_radio3:chargeTime 10
SettingDescription
enableBatterySet to "false" so radios never run out of charge.
batteryLifeHow many minutes a full battery lasts while the radio is on.
transmitDrainHow many times faster the battery drains while talking.
vehicleChargingSet to "false" to stop radios and power banks charging in vehicles.
chargeTimeHow many minutes it takes to fully charge, in a vehicle or from a power bank.

With radio items, every radio keeps its own charge, so it stays the same if it's dropped, traded or swapped. Without items, the charge is saved for each player.

Hands & animations

setr scully_radio3:holdAnimation "true"
setr scully_radio3:allowNoTalkAnimation "true"
setr scully_radio3:screenMirror "true"
setr scully_radio3:disableWhenDead "true"
SettingDescription
holdAnimationSet to "false" so players don't hold a radio while it's open.
allowNoTalkAnimationSet to "false" to remove the "None" talk animation, so everyone has to use one.
screenMirrorSet to "false" to stop the radio's screen showing on the radio in your hand. Only see the screen on the UI.
disableWhenDeadSet to "false" to let dead players keep using their radio.

Data Files

FilePurpose
data/channels.luaPrivate channels and channel names.
data/signal.luaHow distance, buildings, weather and areas affect how clear people sound.
data/jammers.luaWho can use jammers, their range, and jammers that are always on the map.
data/animations.luaTalk animations.
data/colors.luaThe preset colors.
data/designs.luaThe preset designs.
data/leo.luaThe Police app's 10-codes and wellness checks.

Customising

Department channels (data/channels.lua)

Group channels together for each department and lock them so only that department can join. Each group shows up on the radio with its own name, icon and color, and lists its named channels.

return {
    names = {
        [10] = 'Civ Main',
        [100] = 'Taxi'
    },
 
    zones = {
        {
            label = 'Law Enforcement',
            icon = 'shield-star',
            color = '#3b82f6',
            restrict = { 1, 5 },
            access = {
                states = { isLEO = true },
                jobs = { police = 0, sheriff = 0 },
                aces = { 'group.police' },
                onDuty = false
            },
            emergency = true,
            channels = {
                { frequency = 1, label = 'Dispatch' },
                { frequency = 2, label = 'Patrol 1' }
            }
        }
    }
}
OptionDescription
namesNames for public channels anyone can join, like "Taxi" on 100.
zonesThe department groups.
labelThe group's name on the radio.
iconA Tabler icon (opens in a new tab) name.
colorThe group's color on the radio.
restrictThe first and last channel only this group can join, { 1, 5 } locks channels 1 to 5.
access.statesPlayer state bags that let someone in, like isLEO.
access.jobsJobs or gangs that can join, with the lowest grade allowed.
access.acesAce permissions that let someone in.
access.onDutySet to true so players have to be on duty.
emergencySet to true to allow the panic button on these channels.
talkoverOptional, turns talk over protection on or off just for this group.
channelsThe named channels in this group.

Players only need to match one of the access options to get in.

Signal (data/signal.lua)

Every player has a signal strength, and when someone talks, how far apart you are plus both of your signals decides how clear they sound.

OptionDescription
range.clearPeople sound perfect up to this distance.
range.maxPeople sound their worst from this distance.
range.cutOffSet to true so people past the max distance can't be heard at all.
range.heightBoostBeing high up (rooftops, mountains, helicopters) increases the range.
vehicleBoostHow much better your signal is in a vehicle, 0.25 is 25% better.
interiorInterferenceHow bad signal is inside buildings and tunnels, from 0.0 (no effect) to 1.0 (no signal).
underwaterInterferenceHow bad signal is under water.
weatherInterferenceHow bad signal is in each type of weather.
areasBad signal spots, by GTA zone name or a point and radius.
breakupBelow this signal, voices start dropping out. Set to 0.0 to turn it off.

Jammers (data/jammers.lua)

Jammers block every radio around them apart from the channels you allow through. Players place them with the jammer item or command, then walk up to them to open the jammer's own screen, where they can turn it on and off, change its range and pick the allowed channels. You can also interact with the jammer to pick it back up. Anyone can shoot a jammer to destroy it.

return {
    model = `scully_radio3_jammer`,
    item = 'radio_jammer',
    command = 'jammer',
    target = false,
 
    permission = {
        states = { isLEO = true },
        jobs = { police = 0, sheriff = 0 },
        aces = { 'group.jammer' }
    },
 
    range = { min = 10.0, max = 150.0, step = 5.0, default = 40.0 },
 
    defaults = {
        { coords = vec4(0.0, 0.0, 0.0, 0.0), range = 120.0, allowed = { 1, 2 }, prop = true }
    }
}
OptionDescription
modelThe jammer prop.
itemThe jammer item, set to false to place jammers without one. While it's set, placing a jammer always uses up the item, even with the command, and picking a jammer up gives it back.
commandThe command to place a jammer, set to false to turn it off.
targetSet to true to use and pick up jammers with ox_target instead of the help text.
permissionWho can place and use jammers. Players can always use the jammers they placed themselves.
rangeThe smallest, biggest and starting range in meters.
defaultsJammers that are always on the map, like around a prison. These can't be picked up but they can be destroyed. Set prop = false to make one invisible (it can't be destroyed then).

When placing a jammer, look where you want it to go. It shows green when it can go there and red when the ground isn't flat or it's too far away.

Talk animations (data/animations.lua)

Players pick how they hold the radio when they talk in the radio's settings. You can add your own animations, and set exactly where the radio sits in the hand for each one.

return {
    {
        name = 'handheld',
        label = 'Handheld',
        dict = 'anim@scully_radio',
        anim = 'handheld',
        flag = 49,
        prop = true,
        attach = { ... }
    },
    { name = 'ear', label = 'Ear', dict = 'cellphone@', anim = 'cellphone_call_listen_base', flag = 49, prop = true },
    { name = 'default', label = 'Default / Shoulder', pma = true },
    { name = 'none', label = 'None', none = true }
}
OptionDescription
nameA unique name for the animation.
labelThe name players see in the settings.
dict / animThe animation to play while talking.
flag49 only animates the upper body, so players can still walk.
propSet to true to hold the radio while talking.
attach.boneWhich hand holds the radio, 60309 is the left hand and 28422 is the right.
attach.offsetMoves the radio in the hand.
attach.rotationTurns the radio in the hand.
pmaUses pma-voice's own radio animation.
noneNo animation.

The first animation in the list is the one new players start with.

Colors (data/colors.lua)

{ name = 'blue', label = 'Blue', color = '#1f5fd1' },

Designs (data/designs.lua)

{ name = 'woodland', label = 'Woodland Camo', image = 'woodland.svg' },

With itemColors on, every color apart from graphite needs an item called radio_<name>, like radio_blue. There is configuration files provided for both ox_inventory and qb-inventory for every color and design included with the resource along with item images.

Police app (data/leo.lua)

OptionDescription
enabledSet to false to hide the Police app.
codesThe 10-codes officers can set, they show next to their name on the radio.
wellness.enabledSends a wellness check to the officer's channel when they haven't moved for a while.
wellness.minutesHow long an officer has to stand still.
wellness.distanceMoving less than this distance doesn't count.
wellness.requireDutySet to false to check officers that are off duty too.
wellness.blipTimeHow many seconds the wellness check blip stays on the map.

Items

The item lists are in the items folder:

  • items/ox_inventory.lua for ox_inventory.
  • items/qb-inventory.lua for qb-inventory.
  • Copy items/images into your inventory's image folder.
ItemDescription
radioThe radio.
radio_<color> / radio_<design>A radio in that color or design, only needed with itemColors on.
radio_batteryA power bank that charges your radio.
radio_jammerA signal jammer.

Important: Radios and power banks must not stack (stack = false on ox_inventory, unique = true on qb-inventory), otherwise they can't each keep their own charge.

Power banks

Using a power bank plugs it into whichever radio in your inventory has the least charge, and charges it up over time. The power bank stays in your inventory and loses charge as it goes, you can see how much it has left on the item. It stops when the radio is full, the power bank is empty, or you lose either one.

Flat power banks charge back up while you sit in a vehicle.

Add-on packs

Add-on packs add new designs to the radio, and install by dragging them into the resource. They never replace your files, so your config stays exactly how you set it.

Installing a pack

  1. Drag the pack's data, designs, items and stream folders into your scully_radio3 folder, and merge them when asked.
  2. If you use itemColors, add the pack's items from its items/addons file to your inventory and copy its images into your inventory's image folder.
  3. Restart your server.

To remove a pack, delete its files and restart.


Keybinds & Commands

KeybindDefaultDescription
Radio: OpenUOpens the radio.
Radio: Next channelPAGEUPNext channel.
Radio: Previous channelPAGEDOWNPrevious channel.
Radio: Last channelJumps back to the last channel.
Radio: Toggle scanTurns listening to extra channels on and off.
Radio: PowerTurns the radio on and off.
Radio: Volume upTurns the volume up.
Radio: Volume downTurns the volume down.

Players can change these in their GTA key settings under FiveM.

CommandDescription
/radioOpens the radio. Needs a radio item if items are enabled.
/jammerPlaces a jammer, for players that are allowed to. Needs a jammer item if enabled.

Placing a jammer:

ControlDescription
EnterPlace it.
Arrow left / rightTurn it.
BackspaceCancel.

Next to a jammer:

ControlDescription
EOpens the jammer.
GPicks the jammer up.

On the jammer:

ControlDescription
PWR / PTurns the jammer on and off.
MODE / MSwitches between the range and the allowed channels.
< > / arrow keysChanges the range, or flicks through the allowed channels.
0-9 and .Types a channel to allow through.
ENT / EnterAllows the typed channel.
CLR / BackspaceDeletes a number, or removes the allowed channel on screen.
CUR CH / CAllows the channel your radio is on.
EscCloses the jammer.

Exports

Client exports

ExportDescription
isOpen()true while the radio is open.
isPowered()true while the radio is on.
getChannel()The channel the player is on, 0 if none.
getBattery()The battery from 0 to 100.
openRadio(color)Opens the radio. color is optional, a preset name or hex color to lock the radio to.
closeRadio()Closes the radio.
setPower(state)Turns the radio on or off.
joinChannel(frequency)Joins a channel, false if the player isn't allowed on it.
leaveChannel()Leaves the channel.
notify(type, text, icon, duration)Shows a notification on the radio, only while it's on.
setInterference(value)Makes the player's signal worse, from 0.0 to 1.0.
getSignal()The player's signal from 0.0 (none) to 1.0 (perfect).
openJammer()Opens the jammer the player is standing next to, if they're allowed to use it.
pickupJammer()Picks up the jammer the player is standing next to, if they're allowed to.
customJammerInteraction(func)Turns off the jammer help text so you can use your own interaction. func runs once in its own thread, call openJammer() and pickupJammer() from it.
customHelpAlert(show, hide)Swaps out the help text, like the jammer's E prompt. show(text) runs once when it appears and hide() runs once when it goes away.
-- ox_lib text UI example.
exports.scully_radio3:customHelpAlert(function(text)
    lib.showTextUI(text)
end, function()
    lib.hideTextUI()
end)

Server exports

ExportDescription
getPlayerName(source)The name and callsign shown on the radio.
getBattery(source)The player's battery from 0 to 100.
setBattery(source, level)Sets the player's battery from 0 to 100.
exports.scully_radio3:setBattery(source, 100)

Events

Client events:

EventArgumentsDescription
scully_radio3:openedThe radio was opened.
scully_radio3:powerChangedstateThe radio was turned on or off.
scully_radio3:channelChangedfrequency, oldThe player changed channel.
scully_radio3:receivingserverId, state, frequencySomeone started or stopped talking on the player's channel.
scully_radio3:settingsLoadedsettingsThe player's radio settings loaded.
scully_radio3:settingChangedkey, valueThe player changed a setting.

Server events:

EventArgumentsDescription
scully_radio3:playerLoadedsourceA player's radio loaded.
scully_radio3:channelChangedsource, frequency, oldA player changed channel.
scully_radio3:emergencySentsource, frequency, coordsA player pressed the panic button.
scully_radio3:wellnessChecksource, frequency, coordsA wellness check went out for an officer.
AddEventHandler('scully_radio3:emergencySent', function(source, frequency, coords)
    print(('%s pressed their panic button on %s'):format(GetPlayerName(source), frequency))
end)

State bags

State bagTypeDescription
radioPowerPlayertrue while the player's radio is on.
radioBatteryPlayer{ level, serial, item }, the player's battery and the radio it belongs to.
radioInterferencePlayerHow bad the player's signal is, from 0.0 to 1.0.
radioJammersGlobalEvery jammer on the map.

Locales

The languages are in the locales folder. Copy en.lua, translate it and set it in scully_radio3.cfg:

setr scully_radio3:locale "en"

Showcase

https://youtu.be/lKjy3TKmsJI (opens in a new tab)


Support

For support, updates and community discussion, join the Scully's Development community:

https://discord.gg/scully (opens in a new tab)