303 lines
8.7 KiB
Lua
Raw Normal View History

2025-04-07 01:41:12 +00:00
local config = require 'config.client'
local sharedConfig = require 'config.shared'
local radioMenu = false
local onRadio = false
local onChannel = false
local radioChannel = 0
local radioVolume = 50
local Radios = {}
local micClicks = config.defaultMicClicks
local function connectToRadio(channel)
radioChannel = channel
onChannel = true
qbx.playAudio({
audioName = 'Start_Squelch',
audioRef = 'CB_RADIO_SFX',
source = cache.ped
})
exports['pma-voice']:setRadioChannel(channel)
exports['pma-voice']:setVoiceProperty('radioEnabled', true)
if channel % 1 > 0 then
exports.qbx_core:Notify(locale('joined_radio') .. channel .. ' MHz', 'success')
else
exports.qbx_core:Notify(locale('joined_radio') .. channel .. '0 MHz', 'success')
end
end
local function leaveChannel()
if onChannel then
qbx.playAudio({
audioName = 'End_Squelch',
audioRef = 'CB_RADIO_SFX',
source = cache.ped
})
exports.qbx_core:Notify(locale('left_channel'), 'error')
end
radioChannel = 0
onChannel = false
exports['pma-voice']:setRadioChannel(0)
exports['pma-voice']:setVoiceProperty('radioEnabled', false)
end
local function adjustRadioChannel(increment)
if not onRadio then
return false
end
local rchannel = radioChannel + increment
-- Skip restricted channels
while sharedConfig.restrictedChannels[rchannel] do
rchannel += increment
end
rchannel = math.min(math.max(rchannel, 1), config.maxFrequency)
-- Validate the new channel
if not rchannel or type(rchannel) ~= "number" or rchannel > config.maxFrequency or rchannel < 1 then
exports.qbx_core:Notify(locale('invalid_channel'), 'error')
return false
end
rchannel = qbx.math.round(rchannel, config.decimalPlaces)
-- Check if already on the channel
if rchannel == radioChannel then
exports.qbx_core:Notify(locale('on_channel'), 'error')
return false
end
-- Check restricted channel access
local frequency = sharedConfig.whitelistSubChannels and rchannel or math.floor(rchannel)
if sharedConfig.restrictedChannels[frequency] then
local isJobAllowed = sharedConfig.restrictedChannels[frequency][QBX.PlayerData.job.name]
if not (isJobAllowed and QBX.PlayerData.job.onduty) then
exports.qbx_core:Notify(locale('restricted_channel'), 'error')
return false
end
end
-- Set the new channel
radioChannel = rchannel
return true
end
local function toggleRadio(toggle)
radioMenu = toggle
SetNuiFocus(radioMenu, radioMenu)
TriggerServerEvent('qbx_radio:server:setHoldingRadio', radioMenu)
if radioMenu then
lib.playAnim(cache.ped, 'cellphone@', 'cellphone_text_read_base', 2.0, 2.0, -1, 51, 0.0, false, 0, false)
SendNUIMessage({type = 'open'})
else
ClearPedTasks(cache.ped)
SendNUIMessage({type = 'close'})
end
end
local function cleanupRadioProp(serverId)
if not Radios[serverId] then return end
SetEntityAsMissionEntity(Radios[serverId], true, true)
DeleteEntity(Radios[serverId])
Radios[serverId] = nil
end
AddStateBagChangeHandler('isHoldingRadio', '', function(bagName, _, value, _, replicated)
if replicated then return end
local player = GetPlayerFromStateBagName(bagName)
if not player then return end
local serverId = GetPlayerServerId(player)
if value then
local model = lib.requestModel(`prop_cs_hand_radio`)
if not model then return end
local ped = lib.waitFor(function ()
local playerPed = GetPlayerPed(player)
if playerPed > 0 then return playerPed end
end, locale('failed_spawn'), 3000)
local coords = GetEntityCoords(ped)
Radios[serverId] = CreateObject(model, coords.x, coords.y, coords.z, false, false, false)
AttachEntityToEntity(Radios[serverId], ped, GetPedBoneIndex(ped, 28422), -0.01, 0.0, 0.0, 0.0, 0.0, 0.0, true, true, false, true, 1, true)
SetModelAsNoLongerNeeded(model)
else
cleanupRadioProp(serverId)
end
end)
RegisterNetEvent('onPlayerDropped', function(serverId)
cleanupRadioProp(serverId)
end)
AddEventHandler('onResourceStop', function(resource)
if resource ~= cache.resource then return end
for serverId in pairs(Radios) do
cleanupRadioProp(serverId)
end
end)
local function powerButton()
onRadio = not onRadio
if not onRadio then
leaveChannel()
toggleRadio(false)
end
end
local function isRadioOn()
return onRadio
end
exports('IsRadioOn', isRadioOn)
-- Sets mic clicks to the default value when the player logs in.
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
exports['pma-voice']:setVoiceProperty("micClicks", config.defaultMicClicks)
end)
-- Resets state on logout, in case of character change.
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
powerButton()
end)
AddEventHandler('ox_inventory:itemCount', function(itemName, totalCount)
if itemName ~= 'radio' then return end
if totalCount <= 0 and radioChannel ~= 0 then
powerButton()
end
end)
RegisterNetEvent('qbx_radio:client:use', function()
toggleRadio(not radioMenu)
end)
RegisterNetEvent('qbx_radio:client:onRadioDrop', function()
if radioChannel ~= 0 then
powerButton()
end
end)
RegisterNUICallback('joinRadio', function(data, cb)
if not onRadio then return cb('ok') end
local rchannel = tonumber(data.channel)
if not rchannel or type(rchannel) ~= "number" or rchannel > config.maxFrequency or rchannel < 1 then
exports.qbx_core:Notify(locale('invalid_channel'), 'error')
cb('ok')
return
end
rchannel = qbx.math.round(rchannel, config.decimalPlaces)
if rchannel == radioChannel then
exports.qbx_core:Notify(locale('on_channel'), 'error')
cb('ok')
return
end
local frequency = not sharedConfig.whitelistSubChannels and math.floor(rchannel) or rchannel
if sharedConfig.restrictedChannels[frequency] and (not sharedConfig.restrictedChannels[frequency][QBX.PlayerData.job.name] or not QBX.PlayerData.job.onduty) then
exports.qbx_core:Notify(locale('restricted_channel'), 'error')
cb('ok')
return
end
connectToRadio(rchannel)
end)
RegisterNUICallback('leaveChannel', function(_, cb)
if not onRadio then return cb('ok') end
if radioChannel == 0 then
exports.qbx_core:Notify(locale('not_on_channel'), 'error')
else
leaveChannel()
end
cb('ok')
end)
RegisterNUICallback('volumeUp', function(_, cb)
if not onRadio then return cb('ok') end
if radioVolume > 95 then
exports.qbx_core:Notify(locale('max_volume'), 'error')
return
end
radioVolume += 5
exports.qbx_core:Notify(locale('new_volume') .. radioVolume, 'success')
exports['pma-voice']:setRadioVolume(radioVolume)
cb('ok')
end)
RegisterNUICallback('volumeDown', function(_, cb)
if not onRadio then return cb('ok') end
if radioVolume < 10 then
exports.qbx_core:Notify(locale('min_volume'), 'error')
return
end
radioVolume -= 5
exports.qbx_core:Notify(locale('new_volume') .. radioVolume, 'success')
exports['pma-voice']:setRadioVolume(radioVolume)
cb('ok')
end)
RegisterNUICallback('increaseradiochannel', function(_, cb)
if not onRadio then return cb('ok') end
if adjustRadioChannel(1) then
connectToRadio(radioChannel)
cb(radioChannel)
end
end)
RegisterNUICallback('decreaseradiochannel', function(_, cb)
if not onRadio then return cb('ok') end
if adjustRadioChannel(-1) then
connectToRadio(radioChannel)
cb(radioChannel)
end
end)
RegisterNUICallback('toggleClicks', function(_, cb)
if not onRadio then return cb('ok') end
micClicks = not micClicks
exports['pma-voice']:setVoiceProperty("micClicks", micClicks)
qbx.playAudio({
audioName = "Off_High",
audioRef = 'MP_RADIO_SFX',
source = cache.ped
})
exports.qbx_core:Notify(locale('clicks' .. (micClicks and 'On' or 'Off')), micClicks and 'success' or 'error')
cb('ok')
end)
RegisterNUICallback('powerButton', function(_, cb)
qbx.playAudio({
audioName = "On_High",
audioRef = 'MP_RADIO_SFX',
source = cache.ped
})
powerButton()
cb(onRadio and 'on' or 'off')
end)
RegisterNUICallback('escape', function(_, cb)
toggleRadio(false)
cb('ok')
end)
if config.leaveOnDeath then
AddStateBagChangeHandler('isDead', ('player:%s'):format(cache.serverId), function(_, _, value)
if value and onRadio and radioChannel ~= 0 then
leaveChannel()
end
end)
end