# Voice add system

{% hint style="info" %}
Our HUD is already compatible with several voice systems: mumble-voip, pma-voice, saltychat, tokovoip.
{% endhint %}

## Custom Voice System Integration Guide

This guide explains how to integrate your own voice/proximity chat system with PHUD.

### 📋 Table of Contents

* How It Works
* Method 1: Using Event Handlers
* Method 2: Using Polling Loop
* Available Exports
* Real Examples
* Configuration
* Troubleshooting

***

### How It Works

PHUD needs to receive two pieces of information from your voice system:

1. **Voice Range** (number 1-3):
   * `1` = Whisper (short range)
   * `2` = Normal (medium range)
   * `3` = Shouting (long range)
2. **Talking Status** (boolean):
   * `true` = Player is currently talking
   * `false` = Player is not talking

The HUD will automatically update the voice indicator.

***

### Method 1: Using Event Handlers (Recommended)

This method listens to events triggered by your voice system.

#### Step 1: Create Your Integration File

Create a new file in this folder: `your-voice-system.lua`

#### Step 2: Use This Template

```lua
-- Listen to range change events
AddEventHandler('your_voice:setTalkingMode', function(mode)
    if exports['PHud'] then
        exports['PHud']:UpdateVoiceRange(mode) -- mode should be 1, 2, or 3
    end
end)

-- Listen to talking status events
AddEventHandler('your_voice:isTalking', function(talking)
    if exports['PHud'] then
        exports['PHud']:UpdateTalking(talking) -- talking should be true or false
    end
end)
```

#### Real Example: PMA Voice

```lua
-- Listen to mode change
AddEventHandler('pma-voice:setTalkingMode', function(mode)
    if exports['PHud'] then
        exports['PHud']:UpdateVoiceRange(mode)
    end
end)

-- Listen to radio talking
AddEventHandler('pma-voice:radioActive', function(talking)
    if exports['PHud'] then
        exports['PHud']:UpdateTalking(talking)
    end
end)

-- Continuous update loop for real-time status
CreateThread(function()
    while true do
        Wait(100)
        local playerState = LocalPlayer.state
        local currentRange = playerState.proximity and playerState.proximity.mode or 2
        local isSpeaking = MumbleIsPlayerTalking(PlayerId()) or NetworkIsPlayerTalking(PlayerId())

        if exports['PHud'] then
            exports['PHud']:UpdateVoice(currentRange, isSpeaking)
        end
    end
end)
```

***

### Method 2: Using Polling Loop

This method actively checks your voice system at regular intervals.

#### Template

```lua
Citizen.CreateThread(function()
    while true do
        Wait(200) -- Update every 200ms (recommended for voice)

        -- Get voice range from your system
        local voiceRange = exports['your-voice-system']:GetVoiceRange() -- Should return 1, 2, or 3

        -- Check if player is talking
        local isTalking = exports['your-voice-system']:IsTalking() -- Should return true/false

        -- Or use native functions
        -- local isTalking = NetworkIsPlayerTalking(PlayerId())

        -- Update the HUD
        if voiceRange and isTalking ~= nil then
            UpdateVoice(voiceRange, isTalking)
        end
    end
end)
```

#### Real Example: Mumble VOIP

```lua
CreateThread(function()
    while true do
        Wait(100)

        -- Get voice range
        local voiceRange = exports['mumble-voip']:GetVoiceRange() or 2

        -- Check if player is talking using native
        local isTalking = MumbleIsPlayerTalking(PlayerId())

        -- Update HUD
        UpdateVoice(voiceRange, isTalking)
    end
end)
```

***

### Available Exports

Use these functions in your integration file:

#### Update Functions

```lua
-- Update both range and talking status at once
UpdateVoice(range, talking)

-- Update only voice range (1-3)
UpdateVoiceRange(range)

-- Update only talking status (true/false)
UpdateTalking(talking)
```

#### Get Functions

```lua
-- Get current voice range
local range = exports['PHud']:GetVoiceRange()

-- Get current talking status
local talking = exports['PHud']:GetTalking()

-- Get both values
local voice = exports['PHud']:GetVoice()
-- Returns: { range = 2, talking = false }
```

#### Force Set Functions

```lua
-- Force set voice range without checking for changes
exports['PHud']:SetVoiceRange(2)

-- Force set talking status without checking for changes
exports['PHud']:SetTalking(true)
```

***

### Real Examples

#### PMA Voice (Complete Implementation)

```lua
AddEventHandler('pma-voice:setTalkingMode', function(mode)
    if exports['PHud'] then
        exports['PHud']:UpdateVoiceRange(mode)
    end
end)

AddEventHandler('pma-voice:radioActive', function(talking)
    if exports['PHud'] then
        exports['PHud']:UpdateTalking(talking)
    end
end)

CreateThread(function()
    while true do
        Wait(100)
        local playerState = LocalPlayer.state
        local currentRange = playerState.proximity and playerState.proximity.mode or 2
        local isSpeaking = MumbleIsPlayerTalking(PlayerId()) or NetworkIsPlayerTalking(PlayerId())

        if exports['PHud'] then
            exports['PHud']:UpdateVoice(currentRange, isSpeaking)
        end
    end
end)
```

#### Salty Chat

```lua
RegisterNetEvent('SaltyChat_OnSpeakingStateChange')
AddEventHandler('SaltyChat_OnSpeakingStateChange', function(talking)
    UpdateTalking(talking)
end)

RegisterNetEvent('SaltyChat_OnVoiceRangeChange')
AddEventHandler('SaltyChat_OnVoiceRangeChange', function(range, index)
    -- Convert Salty Chat range to 1-3 scale
    local phudRange = index or 2
    UpdateVoiceRange(phudRange)
end)

CreateThread(function()
    while true do
        Wait(200)

        -- Get current range from Salty Chat
        local range = exports['saltychat']:GetVoiceRange() or 2
        local talking = exports['saltychat']:IsTalking() or false

        UpdateVoice(range, talking)
    end
end)
```

#### TokoVOIP

```lua
RegisterNetEvent('tokovoip:setTalkingMode')
AddEventHandler('tokovoip:setTalkingMode', function(mode)
    UpdateVoiceRange(mode)
end)

CreateThread(function()
    while true do
        Wait(150)

        if exports['tokovoip'] then
            local voiceData = exports['tokovoip']:getPlayerData()

            if voiceData then
                local range = voiceData.voiceMode or 2
                local talking = voiceData.talking or false

                UpdateVoice(range, talking)
            end
        end
    end
end)
```

#### Simple Native Implementation (No Voice System)

```lua
CreateThread(function()
    local currentRange = 2 -- Default to normal

    while true do
        Wait(100)

        -- Check if player is talking using native
        local isTalking = NetworkIsPlayerTalking(PlayerId())

        -- Allow manual range switching with a key
        if IsControlJustPressed(0, 20) then -- Z key by default
            currentRange = currentRange + 1
            if currentRange > 3 then
                currentRange = 1
            end
        end

        UpdateVoice(currentRange, isTalking)
    end
end)
```

***

### Configuration

#### Step 1: Create Your Integration File

Create a new `.lua` file in the `phud/editable/voice/` folder.

**Example:**

* Create the file: `phud/editable/voice/custom.lua`
* In config.lua: `Config.VoiceSystem = "custom"`

**Important:** The filename (without `.lua`) must EXACTLY match the value you set in `Config.VoiceSystem`.

#### Step 2: Enable Your System in Config

Open `phud/config.lua` and set:

```lua
-- Use your specific system
Config.VoiceSystem = "custom" -- Filename without .lua extension
```

Available systems:

* `pma-voice` - PMA Voice (default)
* `mumble-voip` - Mumble VOIP
* `saltychat` - Salty Chat
* `tokovoip` - TokoVOIP
* Or your custom filename

#### Step 3: Restart Resource

```
ensure phud
```

***

### Troubleshooting

#### Voice Indicator Not Updating

1. **Check if your file is loaded**
   * Make sure the filename matches `Config.VoiceSystem`
   * File should be in `phud/editable/voice/` folder
2. **Check console for errors**
   * Look for Lua errors in F8 console
   * Check server console for warnings
3. **Verify data format**
   * Range should be 1, 2, or 3
   * Talking should be true or false
   * Use `print()` to debug values
4. **Check events are triggering**

   ```lua
   AddEventHandler('your_event', function(data)
       print("Voice event received:", data)
       -- Your code here
   end)
   ```

#### Talking Status Not Working

* Check if your voice system has a talking event
* Use native function: `NetworkIsPlayerTalking(PlayerId())`
* Try `MumbleIsPlayerTalking(PlayerId())` if using Mumble
* Reduce Wait time in polling loop (try 100ms)

#### Range Not Changing

* Verify your voice system sends range change events
* Check if range values are numbers (1, 2, 3)
* Make sure you're not sending strings ("1" instead of 1)

#### Auto-Detection Not Working

* Set specific system name instead of "auto"
* Check if your voice resource is started before PHUD
* Ensure your voice system resource name matches

***

### Common Native Functions

These native functions work with most voice systems:

```lua
-- Check if local player is talking
NetworkIsPlayerTalking(PlayerId())

-- Check if player is talking via Mumble
MumbleIsPlayerTalking(PlayerId())

-- Get local player ID
PlayerId()

-- Check player state (for pma-voice)
LocalPlayer.state.proximity
LocalPlayer.state.radioChannel
```

***

### Need Help?

If your voice system is not listed here:

1. Find out what events your system triggers
2. Or find out what exports your system provides
3. Or use native functions to detect talking
4. Check your system's documentation for available functions

Common event patterns:

* `system_name:setTalkingMode`
* `system_name:isTalking`
* `system_name:onVoiceChange`
* `system_name:radioActive`

Common export patterns:

* `exports['system_name']:GetVoiceRange()`
* `exports['system_name']:IsTalking()`
* `exports['system_name']:getPlayerData()`

Common native fallbacks:

* `NetworkIsPlayerTalking(PlayerId())`
* `MumbleIsPlayerTalking(PlayerId())`
