303 lines
8.7 KiB
Lua
303 lines
8.7 KiB
Lua
|
|
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
|