# Configuration

## <mark style="color:red;">⚠️ REQUIRED STEPS BEFORE USE</mark>

<mark style="color:red;">**1. pma-voice Configuration in server.cfg**</mark>

Make sure that `voice_enableRadioAnim` in `server.cfg` is set to `0`. If it doesn't exist, add to `server.cfg`:

```
setr voice_enableRadioAnim 0
```

<mark style="color:red;">**2. pma-voice Export**</mark>

Add this to the bottom of `pma-voice/client/module/radio.lua`:

```lua
exports("getRadioData", function()
    return radioData
end)
```

***

<mark style="color:yellow;">Shared Configuration (</mark><mark style="color:yellow;">`configs/shared.lua`</mark><mark style="color:yellow;">)</mark>

**Reserved Channels System**

Controls which radio frequencies are restricted to specific jobs or custom conditions.

```lua
Shared.ReservedChannels = {
    { jobs = { "police", "sheriff", "state" }, channels = { 909, 909.01, 909.02, 911 } },
    { jobs = { "police", "sheriff", "state", "ambulance" }, channels = { 912 } },
    { jobs = { "mechanic1" }, channels = { 913 } },
    { jobs = { "mechanic2" }, channels = { 914 } },
    { channels = { 915 }, handler = function(source, channel)
            local crimeJob = "xd"
            if crimeJob == "vagos" then 
                return true
            else 
                return false, "Access only for Vagos members"
            end
        end
    },
}
```

* **jobs**: Array of job names that have access to the specified channels
* **channels**: Array of frequency numbers that are reserved
* **handler**: Optional custom function for advanced access control
  * **Parameters**: `source` (player ID), `channel` (frequency number)
  * **Returns**: `boolean` (access granted), optional `string` (denial message)
* **Usage**: Add new entries to create job-specific radio channels

**Example - Adding a new reserved channel for mechanics:**

```lua
{ jobs = { "mechanic" }, channels = { 920, 920.01, 920.02 } },
```

***

**Antenna Disable System**

Configuration for the antenna hacking/disabling feature.

```lua
Shared.DisableSettings = {
    enabled = true,
    time = 60000 * 20, -- time in ms that antenna is disabled
    item = "dh_antenna_disabler", -- item required to disable antenna or you can set to false to do not require item
}
```

* **enabled**: Enable/disable the antenna disabling feature
* **time**: Duration in milliseconds that antenna remains disabled (default: 20 minutes)
* **item**: Item name required to disable antenna
  * Set to `false` to allow disabling without an item
  * Default: `"dh_antenna_disabler"`

***

**Antenna Debug Mode**

Shows antenna coverage radius on the map for testing purposes.

```lua
Shared.AntennaDebug = false -- set to true to see antenna radius on map
```

* **AntennaDebug**: Set to `true` to visualize antenna coverage areas on the map
* **Usage**: Enable during setup to verify antenna placement and coverage

***

**Antenna Locations**

Defines all radio antenna locations across the map and their signal coverage.

```lua
Shared.AntennaLocations = {
    {prop = "reh_prop_reh_tablet_01a", coords = vector3(-1267.271, -2442.962, 14.603), rotation = vec3(118.426, -87.740, 122.140), radius = 1500.0},
    {prop = "reh_prop_reh_tablet_01a", coords = vector3(1369.801, -2325.915, 61.777), rotation = vec3(118.426, -87.740, 122.140), radius = 1650.0},
    -- ... more locations
}
```

* **prop**: Prop model name for the antenna object
* **coords**: vector3 coordinates (x, y, z) for antenna placement
* **rotation**: vec3 rotation values (pitch, roll, yaw)
* **radius**: Signal coverage radius in game units
* **Default Locations**: 12 antennas strategically placed across the map
* **Coverage Areas**:
  * Los Santos: 1300-1650 unit radius
  * Sandy Shores: 1350-2000 unit radius
  * Paleto Bay: 1200-3000 unit radius

**Adding a New Antenna:**

```lua
{
    prop = "reh_prop_reh_tablet_01a", 
    coords = vector3(x, y, z), 
    rotation = vec3(pitch, roll, yaw), 
    radius = 1500.0
},
```

***

**Radio Item Name**

Defines the inventory item name for the radio.

```lua
Shared.RadioItem = "radio" -- item name for radio, false to disable
```

* **RadioItem**: Item name in your inventory system
* **Default**: `"radio"`
* **Disable**: Set to `false` to allow radio access without an item requirement

***

#### <mark style="color:yellow;">Client Configuration (</mark><mark style="color:yellow;">`configs/client.lua`</mark><mark style="color:yellow;">)</mark>

**Radio Animations**

Defines multiple animation options for holding and using the radio.

```lua
Config.Animations = {
    { -- Animation 1: Holding Radio (RPEmotes style)
        dict = "anim@male@holding_radio",
        anim = "holding_radio_clip",
        flags = 49,
        propModel = "prop_cs_hand_radio",
        bone = 28422,
        offsets = { x = 0.09, y = 0.03, z = 0.0, xRot = -70.0, yRot = 10.0, zRot = 290.0 },
    },
    { -- Animation 2: Reading (Phone style)
        dict = "cellphone@",
        anim = "cellphone_text_read_base",
        flags = 49,
        propModel = "prop_cs_hand_radio",
        bone = 28422,
        offsets = { x = 0.0, y = 0.0, z = 0.0, xRot = 0.0, yRot = 0.0, zRot = 0.0 },
    },
    { -- Animation 3: Generic Radio Chatter
        dict = "random@arrests",
        anim = "generic_radio_chatter",
        flags = 49,
        propModel = false,
        bone = 28422,
        offsets = { x = 0.0, y = 0.0, z = 0.0, xRot = 0.0, yRot = 0.0, zRot = 0.0 },
    },
    { -- Animation 4: Listening
        dict = "cellphone@",
        anim = "cellphone_call_listen_base",
        flags = 49,
        propModel = "prop_cs_hand_radio",
        bone = 28422,
        offsets = { x = 0.0, y = 0.0, z = 0.0, xRot = 0.0, yRot = 0.0, zRot = 0.0 },
    },
}
```

* **dict**: Animation dictionary name
* **anim**: Specific animation name within the dictionary
* **flags**: Animation flags (49 = upperbody + loop)
* **propModel**: Radio prop model (set to `false` for no prop)
* **bone**: Bone ID for prop attachment (28422 = right hand)
* **offsets**: Position and rotation offsets for the prop
  * **x, y, z**: Position offset
  * **xRot, yRot, zRot**: Rotation offset in degrees
* **Players can switch between animations in-game settings**

***

**Aiming Animation**

Special animation used when player is aiming with a weapon while using radio.

```lua
Config.AimingAnim = {
    dict = "arrest",
    anim = "radio_chatter",
    flags = 49,
    propModel = false,
    bone = 28422,
    offsets = { x = 0.0, y = 0.0, z = 0.0, xRot = 0.0, yRot = 0.0, zRot = 0.0 },
}
```

* **Same structure as regular animations**
* **No prop model by default** for compatibility with weapon aiming
* **Automatically activates when player aims while radio is open**

***

#### <mark style="color:yellow;">Translation Configuration (</mark><mark style="color:yellow;">`configs/translation.lua`</mark><mark style="color:yellow;">)</mark>

Customize all user-facing text in the resource. Full multi-language support.

**Tuning Minigame Translations**

```lua
Shared.Lang = {
    ['tuning_signal_tuning'] = "SIGNAL TUNING",
    ['tuning_description'] = "STABILIZE THE DISTORTED FREQUENCY SIGNAL",
    ['tuning_instructions'] = "Match your signal to the ghost reference line...",
    -- ... more tuning translations
}
```

***

#### <mark style="color:yellow;">Items Configuration (</mark><mark style="color:yellow;">`items/items.lua`</mark><mark style="color:yellow;">)</mark>

Add these items to your inventory system:

**Radio Item**

```lua
-- ESX Example
['radio'] = {
    label = 'Radio',
    weight = 1,
    stack = false,
    close = true,
    description = 'A portable radio communication device'
}
```

**Antenna Disabler Item**

```lua
-- ESX Example
['dh_antenna_disabler'] = {
    label = 'Antenna Disabler',
    weight = 1,
    stack = true,
    close = true,
    description = 'Device to temporarily disable radio antennas'
}
```

**Item Images:**

* `items/dh_antenna_disabler.png` - Provided in the resource
* Add to your inventory images folder

***

#### <mark style="color:yellow;">Customization Examples</mark>

**Creating Job-Specific Channels**

Add multiple job groups with shared access:

```lua
Shared.ReservedChannels = {
    -- Emergency Services (Police + EMS)
    { jobs = { "police", "sheriff", "ambulance" }, channels = { 911, 911.01, 911.02 } },
    
    -- Police Only
    { jobs = { "police", "sheriff", "state" }, channels = { 909, 909.1, 909.2 } },
    
    -- Medical Only
    { jobs = { "ambulance", "doctor" }, channels = { 912 } },
    
    -- Mechanics
    { jobs = { "mechanic" }, channels = { 913, 913.1 } },
    
    -- Government
    { jobs = { "government", "mayor" }, channels = { 920 } },
}
```

***

**Custom Access Handler Example**

Create advanced access control based on custom logic:

```lua
{ 
    channels = { 915, 916, 917 }, 
    handler = function(source, channel)
        local xPlayer = ESX.GetPlayerFromId(source)
        
        -- Check gang affiliation
        local gang = xPlayer.getGang() -- Your gang system
        
        if gang == "vagos" and channel == 915 then
            return true
        elseif gang == "ballas" and channel == 916 then
            return true
        elseif gang == "families" and channel == 917 then
            return true
        else
            return false, "This channel is restricted to gang members"
        end
    end
},
```

***

**Making Radio Accessible Without Item**

Allow all players to use radio without needing the item:

```lua
Shared.RadioItem = false  -- Disable item requirement
```

***

#### <mark style="color:yellow;">Advanced Configuration</mark>

**Frequency Range**

The radio supports frequencies from **0.00 to 9999.99 MHz**.

***

**Signal Strength System**

Signal strength is calculated based on:

1. **Distance from nearest antenna**
2. **Antenna radius configuration**
3. **Whether antenna is disabled**

**Signal Levels:**

* **5 bars**: Within optimal range (< 50% of radius)
* **3-4 bars**: Medium range (50-75% of radius)
* **1-2 bars**: Weak signal (75-100% of radius)
* **0 bars**: No signal (outside radius or antenna disabled)

***

**UI Customization**

Players can customize their radio interface:

* **Wallpaper**: None or Custom
* **Background Opacity**: Adjustable transparency
* **Radio Volume**: Individual volume control
* **Button Click Volume**: UI sound control
* **HUD Display**: Toggle on/off
* **Radio Scale**: Size adjustment
* **HUD Scale**: HUD size adjustment
* **Position**: Drag and reposition UI elements
* **Animation**: Choose from available animations

***

#### <mark style="color:yellow;">Troubleshooting</mark>

**Radio not opening:**

* Verify player has the radio item (if `Shared.RadioItem` is set)
* Check F8 console for Lua errors
* Check if resource is started: `/ensure devhub_radio`

**No signal everywhere:**

* Enable `Shared.AntennaDebug = true` to visualize coverage
* Verify antenna coordinates are valid
* Ensure antenna props are spawning correctly

**Reserved channels not working:**

* Verify job names match your framework's job names exactly
* Check player's job with `/job` or similar command
* Review server console for access denial messages
* Test custom handler functions with debug prints

**Antenna hacking not working:**

* Verify player has `dh_antenna_disabler` item
* Check if `Shared.DisableSettings.enabled` is `true`
* Ensure minigame scripts are loaded
* Look for errors during antenna interaction

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.devhub.gg/radio/configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
