├── off.ogg ├── on.ogg ├── fxmanifest.lua ├── index.html ├── LICENSE ├── config.lua ├── README.md └── client.lua /off.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvarianKnight/rp-radio/HEAD/off.ogg -------------------------------------------------------------------------------- /on.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvarianKnight/rp-radio/HEAD/on.ogg -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version "adamant" 2 | game "gta5" 3 | 4 | name "rp-radio" 5 | description "An in-game radio which makes use of the pma-voice radio API for FiveM" 6 | author "Frazzle (frazzle9999@gmail.com)" 7 | version "2.2.1" 8 | 9 | ui_page "index.html" 10 | 11 | dependencies { 12 | "pma-voice", 13 | } 14 | 15 | files { 16 | "index.html", 17 | "on.ogg", 18 | "off.ogg", 19 | } 20 | 21 | client_scripts { 22 | "config.lua", 23 | "client.lua", 24 | } 25 | 26 | server_scripts { 27 | "server.lua", 28 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | rp-radio 5 | 6 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Fraser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | radioConfig = { 2 | Controls = { 3 | Activator = { -- Open/Close Radio 4 | Name = "INPUT_REPLAY_START_STOP_RECORDING_SECONDARY", -- Control name 5 | Key = 289, -- F2 6 | }, 7 | Secondary = { 8 | Name = "INPUT_SPRINT", 9 | Key = 21, -- Left Shift 10 | Enabled = true, -- Require secondary to be pressed to open radio with Activator 11 | }, 12 | Toggle = { -- Toggle radio on/off 13 | Name = "INPUT_CONTEXT", -- Control name 14 | Key = 51, -- E 15 | }, 16 | Increase = { -- Increase Frequency 17 | Name = "INPUT_CELLPHONE_RIGHT", -- Control name 18 | Key = 175, -- Right Arrow 19 | Pressed = false, 20 | }, 21 | Decrease = { -- Decrease Frequency 22 | Name = "INPUT_CELLPHONE_LEFT", -- Control name 23 | Key = 174, -- Left Arrow 24 | Pressed = false, 25 | }, 26 | Input = { -- Choose Frequency 27 | Name = "INPUT_FRONTEND_ACCEPT", -- Control name 28 | Key = 201, -- Enter 29 | Pressed = false, 30 | }, 31 | Broadcast = { 32 | Name = "INPUT_VEH_PUSHBIKE_SPRINT", -- Control name 33 | Key = 137, -- Caps Lock 34 | }, 35 | ToggleClicks = { -- Toggle radio click sounds 36 | Name = "INPUT_SELECT_WEAPON", -- Control name 37 | Key = 37, -- Tab 38 | } 39 | }, 40 | Frequency = { 41 | Private = { -- List of private frequencies 42 | [1] = true, -- Make 1 a private frequency 43 | }, -- List of private frequencies 44 | Current = 1, -- Don't touch 45 | CurrentIndex = 1, -- Don't touch 46 | Min = 1, -- Minimum frequency 47 | Max = 1000, -- Max number of frequencies 48 | List = {}, -- Frequency list, Don't touch 49 | Access = {}, -- List of freqencies a player has access to 50 | }, 51 | AllowRadioWhenClosed = false -- Allows the radio to be used when not open (uses police radio animation) 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Deprecated you should use [pma-radio](https://github.com/AvarianKnight/pma-radio/) instead. 2 | 3 | # rp-radio 4 | All credits go to Frazzle for making this, this is just an adapted version for pma-voice. 5 | 6 | #### Note 7 | By default the radio is disabled (its meant to be used as an in-game item) to give players the radio by default in the client.lua at the top change `Radio.Has` to `true`, if you would like to make it an item look at the replies on the FiveM forum post, there is a tutorial for adding it as an ESX item. 8 | 9 | The export that is used to give/take a players radio is `exports["rp-radio"]:SetRadio(true/false)` or the event `Radio.Set` 10 | 11 | ### Exports 12 | Getters 13 | 14 | | Export | Description | Return type | 15 | | ---------------- | --------------------------------------------------- | ----------- | 16 | | IsRadioOpen | Check if player is holding radio | bool | 17 | | IsRadioOn | Check if radio is switched on | bool | 18 | | IsRadioAvailable | Check if player has a radio | bool | 19 | | IsRadioEnabled | Check if radio is enabled | bool | 20 | | CanRadioBeUsed | Check if radio can be used | bool | 21 | 22 | Setters 23 | 24 | | Export | Description | Parameter(s) | 25 | | ------------------------------- | ----------------------------------------------------------- | ------------- | 26 | | SetRadioEnabled | Set if the radio is enabled or not | bool | 27 | | SetRadio | Set if player has a radio or not | bool | 28 | | SetAllowRadioWhenClosed | Allow player to broadcast when closed | bool | 29 | | AddPrivateFrequency | Make a frequency private | int | 30 | | RemovePrivateFrequency | Make a private frequency public | int | 31 | | GivePlayerAccessToFrequency | Give a player access to use a private frequency | int | 32 | | RemovePlayerAccessToFrequency | Remove a players access to use a private frequency | int | 33 | | GivePlayerAccessToFrequencies | Give a player access to use multiple private frequencies | int, int, ... | 34 | | RemovePlayerAccessToFrequencies | Remove a players access to use multiple private frequencies | int, int, ... | 35 | 36 | ### Commands 37 | 38 | | Command | Description | 39 | | ---------- | ------------------------ | 40 | | /radio | Open/close the radio | 41 | | /frequency | Choose radio frequency | 42 | 43 | ### Events 44 | 45 | | Event | Description | Paramters(s) | 46 | | ------------ | -------------------------------- | ---------------------- | 47 | | Radio.Toggle | Opens/close the radio | none | 48 | | Radio.Set | Set if player has a radio or not | bool | 49 | 50 | ### Preview 51 | 52 | - [MP4](https://imgur.com/bAT0mls) 53 | -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | local Radio = { 2 | Has = true, 3 | Open = false, 4 | On = false, 5 | Enabled = true, 6 | Handle = nil, 7 | Prop = GetHashKey('prop_cs_hand_radio'), -- only ran once and doesn't break my syntax viewer 8 | Bone = 28422, 9 | Offset = vector3(0.0, 0.0, 0.0), 10 | Rotation = vector3(0.0, 0.0, 0.0), 11 | Dictionary = { 12 | "cellphone@", 13 | "cellphone@in_car@ds", 14 | "cellphone@str", 15 | "random@arrests", 16 | }, 17 | Animation = { 18 | "cellphone_text_in", 19 | "cellphone_text_out", 20 | "cellphone_call_listen_a", 21 | "generic_radio_chatter", 22 | }, 23 | Clicks = true, -- Radio clicks 24 | } 25 | Radio.Labels = { 26 | { "FRZL_RADIO_HELP", "~s~" .. (radioConfig.Controls.Secondary.Enabled and "~" .. radioConfig.Controls.Secondary.Name .. "~ + ~" .. radioConfig.Controls.Activator.Name .. "~" or "~" .. radioConfig.Controls.Activator.Name .. "~") .. " to hide.~n~~" .. radioConfig.Controls.Toggle.Name .. "~ to turn radio ~g~on~s~.~n~~" .. radioConfig.Controls.Decrease.Name .. "~ or ~" .. radioConfig.Controls.Increase.Name .. "~ to switch frequency~n~~" .. radioConfig.Controls.Input.Name .. "~ to choose frequency~n~~" .. radioConfig.Controls.ToggleClicks.Name .. "~ to ~a~ mic clicks~n~Frequency: ~1~ MHz" }, 27 | { "FRZL_RADIO_HELP2", "~s~" .. (radioConfig.Controls.Secondary.Enabled and "~" .. radioConfig.Controls.Secondary.Name .. "~ + ~" .. radioConfig.Controls.Activator.Name .. "~" or "~" .. radioConfig.Controls.Activator.Name .. "~") .. " to hide.~n~~" .. radioConfig.Controls.Toggle.Name .. "~ to turn radio ~r~off~s~.~n~~" .. radioConfig.Controls.Broadcast.Name .. "~ to broadcast.~n~Frequency: ~1~ MHz" }, 28 | { "FRZL_RADIO_INPUT", "Enter Frequency" }, 29 | } 30 | local unarmed = GetHashKey('weapon_unarmed') 31 | Radio.Commands = { 32 | { 33 | Enabled = true, -- Add a command to be able to open/close the radio 34 | Name = "radio", -- Command name 35 | Help = "Toggle hand radio", -- Command help shown in chatbox when typing the command 36 | Params = {}, 37 | Handler = function(src, args, raw) 38 | local playerPed = PlayerPedId() 39 | local isFalling = IsPedFalling(playerPed) 40 | local isDead = IsEntityDead(playerPed) 41 | 42 | if not isFalling and Radio.Enabled and Radio.Has and not isDead then 43 | Radio:Toggle(not Radio.Open) 44 | elseif (Radio.Open or Radio.On) and ((not Radio.Enabled) or (not Radio.Has) or isDead) then 45 | Radio:Toggle(false) 46 | Radio.On = false 47 | Radio:Remove() 48 | exports["pma-voice"]:setVoiceProperty("radioEnabled", false) 49 | elseif Radio.Open and isFalling then 50 | Radio:Toggle(false) 51 | end 52 | end, 53 | }, 54 | { 55 | Enabled = true, -- Add a command to choose radio frequency 56 | Name = "frequency", -- Command name 57 | Help = "Change radio frequency", -- Command help shown in chatbox when typing the command 58 | Params = { 59 | {name = "number", "Enter frequency"} 60 | }, 61 | Handler = function(src, args, raw) 62 | if Radio.Has then 63 | if args[1] then 64 | local newFrequency = tonumber(args[1]) 65 | if newFrequency then 66 | local minFrequency = radioConfig.Frequency.List[1] 67 | if newFrequency >= minFrequency and newFrequency <= radioConfig.Frequency.List[#radioConfig.Frequency.List] and newFrequency == math.floor(newFrequency) then 68 | if not radioConfig.Frequency.Private[newFrequency] or radioConfig.Frequency.Access[newFrequency] then 69 | local idx = nil 70 | 71 | for i = 1, #radioConfig.Frequency.List do 72 | if radioConfig.Frequency.List[i] == newFrequency then 73 | idx = i 74 | break 75 | end 76 | end 77 | 78 | if idx ~= nil then 79 | if Radio.Enabled then 80 | Radio:Remove() 81 | end 82 | 83 | radioConfig.Frequency.CurrentIndex = idx 84 | radioConfig.Frequency.Current = newFrequency 85 | 86 | if Radio.On then 87 | Radio:Add(radioConfig.Frequency.Current) 88 | end 89 | end 90 | end 91 | end 92 | end 93 | end 94 | end 95 | end, 96 | }, 97 | } 98 | 99 | -- Setup each radio command if enabled 100 | for i = 1, #Radio.Commands do 101 | if Radio.Commands[i].Enabled then 102 | RegisterCommand(Radio.Commands[i].Name, Radio.Commands[i].Handler, false) 103 | TriggerEvent("chat:addSuggestion", "/" .. Radio.Commands[i].Name, Radio.Commands[i].Help, Radio.Commands[i].Params) 104 | end 105 | end 106 | 107 | -- Create/Destroy handheld radio object 108 | function Radio:Toggle(toggle) 109 | local playerPed = PlayerPedId() 110 | local count = 0 111 | 112 | if not self.Has or IsEntityDead(playerPed) then 113 | self.Open = false 114 | 115 | DetachEntity(self.Handle, true, false) 116 | DeleteEntity(self.Handle) 117 | 118 | return 119 | end 120 | 121 | if self.Open == toggle then 122 | return 123 | end 124 | 125 | self.Open = toggle 126 | 127 | if self.On and not radioConfig.AllowRadioWhenClosed then 128 | exports["pma-voice"]:setVoiceProperty("radioEnabled", toggle) 129 | end 130 | 131 | local dictionaryType = 1 + (IsPedInAnyVehicle(playerPed, false) and 1 or 0) 132 | local animationType = 1 + (self.Open and 0 or 1) 133 | local dictionary = self.Dictionary[dictionaryType] 134 | local animation = self.Animation[animationType] 135 | 136 | RequestAnimDict(dictionary) 137 | 138 | while not HasAnimDictLoaded(dictionary) do 139 | Citizen.Wait(150) 140 | end 141 | 142 | if self.Open then 143 | RequestModel(self.Prop) 144 | 145 | while not HasModelLoaded(self.Prop) do 146 | Citizen.Wait(150) 147 | end 148 | 149 | self.Handle = CreateObject(self.Prop, 0.0, 0.0, 0.0, true, true, false) 150 | 151 | local bone = GetPedBoneIndex(playerPed, self.Bone) 152 | 153 | SetCurrentPedWeapon(playerPed, unarmed, true) 154 | AttachEntityToEntity(self.Handle, playerPed, bone, self.Offset.x, self.Offset.y, self.Offset.z, self.Rotation.x, self.Rotation.y, self.Rotation.z, true, false, false, false, 2, true) 155 | 156 | SetModelAsNoLongerNeeded(self.Handle) 157 | 158 | TaskPlayAnim(playerPed, dictionary, animation, 4.0, -1, -1, 50, 0, false, false, false) 159 | else 160 | TaskPlayAnim(playerPed, dictionary, animation, 4.0, -1, -1, 50, 0, false, false, false) 161 | 162 | Citizen.Wait(700) 163 | 164 | StopAnimTask(playerPed, dictionary, animation, 1.0) 165 | 166 | NetworkRequestControlOfEntity(self.Handle) 167 | 168 | while not NetworkHasControlOfEntity(self.Handle) and count < 5000 do 169 | Citizen.Wait(0) 170 | count = count + 1 171 | end 172 | 173 | DetachEntity(self.Handle, true, false) 174 | DeleteEntity(self.Handle) 175 | end 176 | end 177 | 178 | -- Add player to radio channel 179 | function Radio:Add(id) 180 | exports["pma-voice"]:setRadioChannel(id) 181 | end 182 | 183 | -- Remove player from radio channel 184 | function Radio:Remove() 185 | exports["pma-voice"]:setRadioChannel(0) 186 | end 187 | 188 | -- Increase radio frequency 189 | function Radio:Decrease() 190 | if self.On then 191 | if radioConfig.Frequency.CurrentIndex - 1 < 1 and radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] == radioConfig.Frequency.Current then 192 | self:Remove(radioConfig.Frequency.Current) 193 | radioConfig.Frequency.CurrentIndex = #radioConfig.Frequency.List 194 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 195 | self:Add(radioConfig.Frequency.Current) 196 | elseif radioConfig.Frequency.CurrentIndex - 1 < 1 and radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] ~= radioConfig.Frequency.Current then 197 | self:Remove(radioConfig.Frequency.Current) 198 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 199 | self:Add(radioConfig.Frequency.Current) 200 | else 201 | self:Remove(radioConfig.Frequency.Current) 202 | radioConfig.Frequency.CurrentIndex = radioConfig.Frequency.CurrentIndex - 1 203 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 204 | self:Add(radioConfig.Frequency.Current) 205 | end 206 | else 207 | if radioConfig.Frequency.CurrentIndex - 1 < 1 and radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] == radioConfig.Frequency.Current then 208 | radioConfig.Frequency.CurrentIndex = #radioConfig.Frequency.List 209 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 210 | elseif radioConfig.Frequency.CurrentIndex - 1 < 1 and radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] ~= radioConfig.Frequency.Current then 211 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 212 | else 213 | radioConfig.Frequency.CurrentIndex = radioConfig.Frequency.CurrentIndex - 1 214 | 215 | if radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] == radioConfig.Frequency.Current then 216 | radioConfig.Frequency.CurrentIndex = radioConfig.Frequency.CurrentIndex - 1 217 | end 218 | 219 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 220 | end 221 | end 222 | end 223 | 224 | -- Decrease radio frequency 225 | function Radio:Increase() 226 | if self.On then 227 | if radioConfig.Frequency.CurrentIndex + 1 > #radioConfig.Frequency.List then 228 | self:Remove(radioConfig.Frequency.Current) 229 | radioConfig.Frequency.CurrentIndex = 1 230 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 231 | self:Add(radioConfig.Frequency.Current) 232 | else 233 | self:Remove(radioConfig.Frequency.Current) 234 | radioConfig.Frequency.CurrentIndex = radioConfig.Frequency.CurrentIndex + 1 235 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 236 | self:Add(radioConfig.Frequency.Current) 237 | end 238 | else 239 | if #radioConfig.Frequency.List == radioConfig.Frequency.CurrentIndex + 1 then 240 | if radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex + 1] == radioConfig.Frequency.Current then 241 | radioConfig.Frequency.CurrentIndex = radioConfig.Frequency.CurrentIndex + 1 242 | end 243 | end 244 | 245 | if radioConfig.Frequency.CurrentIndex + 1 > #radioConfig.Frequency.List then 246 | radioConfig.Frequency.CurrentIndex = 1 247 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 248 | else 249 | radioConfig.Frequency.CurrentIndex = radioConfig.Frequency.CurrentIndex + 1 250 | radioConfig.Frequency.Current = radioConfig.Frequency.List[radioConfig.Frequency.CurrentIndex] 251 | end 252 | end 253 | end 254 | 255 | -- Generate list of available frequencies 256 | function GenerateFrequencyList() 257 | radioConfig.Frequency.List = {} 258 | 259 | for i = radioConfig.Frequency.Min, radioConfig.Frequency.Max do 260 | if not radioConfig.Frequency.Private[i] or radioConfig.Frequency.Access[i] then 261 | radioConfig.Frequency.List[#radioConfig.Frequency.List + 1] = i 262 | end 263 | end 264 | end 265 | 266 | -- Check if radio is open 267 | function IsRadioOpen() 268 | return Radio.Open 269 | end 270 | 271 | -- Check if radio is switched on 272 | function IsRadioOn() 273 | return Radio.On 274 | end 275 | 276 | -- Check if player has radio 277 | function IsRadioAvailable() 278 | return Radio.Has 279 | end 280 | 281 | -- Check if radio is enabled or not 282 | function IsRadioEnabled() 283 | return not Radio.Enabled 284 | end 285 | 286 | -- Check if radio can be used 287 | function CanRadioBeUsed() 288 | return Radio.Has and Radio.On and Radio.Enabled 289 | end 290 | 291 | -- Set if the radio is enabled or not 292 | function SetRadioEnabled(value) 293 | if type(value) == "string" then 294 | value = value == "true" 295 | elseif type(value) == "number" then 296 | value = value == 1 297 | end 298 | 299 | Radio.Enabled = value and true or false 300 | end 301 | 302 | -- Set if player has a radio or not 303 | function SetRadio(value) 304 | if type(value) == "string" then 305 | value = value == "true" 306 | elseif type(value) == "number" then 307 | value = value == 1 308 | end 309 | 310 | Radio.Has = value and true or false 311 | end 312 | 313 | -- Set if player has access to use the radio when closed 314 | function SetAllowRadioWhenClosed(value) 315 | radioConfig.AllowRadioWhenClosed = value 316 | 317 | if Radio.On and not Radio.Open and radioConfig.AllowRadioWhenClosed then 318 | exports["pma-voice"]:setVoiceProperty("radioEnabled", true) 319 | end 320 | end 321 | 322 | -- Add new frequency 323 | function AddPrivateFrequency(value) 324 | local frequency = tonumber(value) 325 | 326 | if frequency ~= nil then 327 | if not radioConfig.Frequency.Private[frequency] then -- Only add new frequencies 328 | radioConfig.Frequency.Private[frequency] = true 329 | 330 | GenerateFrequencyList() 331 | end 332 | end 333 | end 334 | 335 | -- Remove private frequency 336 | function RemovePrivateFrequency(value) 337 | local frequency = tonumber(value) 338 | 339 | if frequency ~= nil then 340 | if radioConfig.Frequency.Private[frequency] then -- Only remove existing frequencies 341 | radioConfig.Frequency.Private[frequency] = nil 342 | 343 | GenerateFrequencyList() 344 | end 345 | end 346 | end 347 | 348 | -- Give access to a frequency 349 | function GivePlayerAccessToFrequency(value) 350 | local frequency = tonumber(value) 351 | 352 | if frequency ~= nil then 353 | if radioConfig.Frequency.Private[frequency] then -- Check if frequency exists 354 | if not radioConfig.Frequency.Access[frequency] then -- Only add new frequencies 355 | radioConfig.Frequency.Access[frequency] = true 356 | 357 | GenerateFrequencyList() 358 | end 359 | end 360 | end 361 | end 362 | 363 | -- Remove access to a frequency 364 | function RemovePlayerAccessToFrequency(value) 365 | local frequency = tonumber(value) 366 | 367 | if frequency ~= nil then 368 | if radioConfig.Frequency.Access[frequency] then -- Check if player has access to frequency 369 | radioConfig.Frequency.Access[frequency] = nil 370 | 371 | GenerateFrequencyList() 372 | end 373 | end 374 | end 375 | 376 | -- Give access to multiple frequencies 377 | function GivePlayerAccessToFrequencies(...) 378 | local frequencies = { ... } 379 | local newFrequencies = {} 380 | 381 | for i = 1, #frequencies do 382 | local frequency = tonumber(frequencies[i]) 383 | 384 | if frequency ~= nil then 385 | if radioConfig.Frequency.Private[frequency] then -- Check if frequency exists 386 | if not radioConfig.Frequency.Access[frequency] then -- Only add new frequencies 387 | newFrequencies[#newFrequencies + 1] = frequency 388 | end 389 | end 390 | end 391 | end 392 | 393 | if #newFrequencies > 0 then 394 | for i = 1, #newFrequencies do 395 | radioConfig.Frequency.Access[newFrequencies[i]] = true 396 | end 397 | 398 | GenerateFrequencyList() 399 | end 400 | end 401 | 402 | -- Remove access to multiple frequencies 403 | function RemovePlayerAccessToFrequencies(...) 404 | local frequencies = { ... } 405 | local removedFrequencies = {} 406 | 407 | for i = 1, #frequencies do 408 | local frequency = tonumber(frequencies[i]) 409 | 410 | if frequency ~= nil then 411 | if radioConfig.Frequency.Access[frequency] then -- Check if player has access to frequency 412 | removedFrequencies[#removedFrequencies + 1] = frequency 413 | end 414 | end 415 | end 416 | 417 | if #removedFrequencies > 0 then 418 | for i = 1, #removedFrequencies do 419 | radioConfig.Frequency.Access[removedFrequencies[i]] = nil 420 | end 421 | 422 | GenerateFrequencyList() 423 | end 424 | end 425 | 426 | -- Define exports 427 | exports("IsRadioOpen", IsRadioOpen) 428 | exports("IsRadioOn", IsRadioOn) 429 | exports("IsRadioAvailable", IsRadioAvailable) 430 | exports("IsRadioEnabled", IsRadioEnabled) 431 | exports("CanRadioBeUsed", CanRadioBeUsed) 432 | exports("SetRadioEnabled", SetRadioEnabled) 433 | exports("SetRadio", SetRadio) 434 | exports("SetAllowRadioWhenClosed", SetAllowRadioWhenClosed) 435 | exports("AddPrivateFrequency", AddPrivateFrequency) 436 | exports("RemovePrivateFrequency", RemovePrivateFrequency) 437 | exports("GivePlayerAccessToFrequency", GivePlayerAccessToFrequency) 438 | exports("RemovePlayerAccessToFrequency", RemovePlayerAccessToFrequency) 439 | exports("GivePlayerAccessToFrequencies", GivePlayerAccessToFrequencies) 440 | exports("RemovePlayerAccessToFrequencies", RemovePlayerAccessToFrequencies) 441 | 442 | local isBroadcasting = false 443 | 444 | AddEventHandler('pma-voice:radioActive', function(broadCasting) 445 | isBroadcasting = broadCasting 446 | end) 447 | 448 | Citizen.CreateThread(function() 449 | -- Add Labels 450 | for i = 1, #Radio.Labels do 451 | AddTextEntry(Radio.Labels[i][1], Radio.Labels[i][2]) 452 | end 453 | 454 | GenerateFrequencyList() 455 | 456 | while true do 457 | Citizen.Wait(0) 458 | -- Init local vars 459 | local playerPed = PlayerPedId() 460 | local isActivatorPressed = IsControlJustPressed(0, radioConfig.Controls.Activator.Key) 461 | local isSecondaryPressed = (radioConfig.Controls.Secondary.Enabled == false and true or IsControlPressed(0, radioConfig.Controls.Secondary.Key)) 462 | local isFalling = IsPedFalling(playerPed) 463 | local isDead = IsEntityDead(playerPed) 464 | local minFrequency = radioConfig.Frequency.List[1] 465 | local broadcastType = 3 + (radioConfig.AllowRadioWhenClosed and 1 or 0) + ((Radio.Open and radioConfig.AllowRadioWhenClosed) and -1 or 0) 466 | local broadcastDictionary = Radio.Dictionary[broadcastType] 467 | local broadcastAnimation = Radio.Animation[broadcastType] 468 | local isPlayingBroadcastAnim = IsEntityPlayingAnim(playerPed, broadcastDictionary, broadcastAnimation, 3) 469 | 470 | -- Open radio settings 471 | if isActivatorPressed and isSecondaryPressed and not isFalling and Radio.Enabled and Radio.Has and not isDead then 472 | Radio:Toggle(not Radio.Open) 473 | elseif (Radio.Open or Radio.On) and ((not Radio.Enabled) or (not Radio.Has) or isDead) then 474 | Radio:Remove() 475 | exports["pma-voice"]:setVoiceProperty("radioEnabled", false) 476 | Radio:Toggle(false) 477 | Radio.On = false 478 | elseif Radio.Open and isFalling then 479 | Radio:Toggle(false) 480 | end 481 | 482 | -- Remove player from private frequency that they don't have access to 483 | if not radioConfig.Frequency.Access[radioConfig.Frequency.Current] and radioConfig.Frequency.Private[radioConfig.Frequency.Current] then 484 | if Radio.On then 485 | Radio:Remove() 486 | end 487 | 488 | radioConfig.Frequency.CurrentIndex = 1 489 | radioConfig.Frequency.Current = minFrequency 490 | 491 | if Radio.On then 492 | Radio:Add(radioConfig.Frequency.Current) 493 | end 494 | end 495 | 496 | -- Check if player is holding radio 497 | if Radio.Open then 498 | local dictionaryType = 1 + (IsPedInAnyVehicle(playerPed, false) and 1 or 0) 499 | local openDictionary = Radio.Dictionary[dictionaryType] 500 | local openAnimation = Radio.Animation[1] 501 | local isPlayingOpenAnim = IsEntityPlayingAnim(playerPed, openDictionary, openAnimation, 3) 502 | local hasWeapon, currentWeapon = GetCurrentPedWeapon(playerPed, 1) 503 | 504 | -- Remove weapon in hand as we are using the radio 505 | if currentWeapon ~= unarmed then 506 | SetCurrentPedWeapon(playerPed, unarmed, true) 507 | end 508 | 509 | -- Display help text 510 | BeginTextCommandDisplayHelp(Radio.Labels[Radio.On and 2 or 1][1]) 511 | 512 | if not Radio.On then 513 | AddTextComponentSubstringPlayerName(Radio.Clicks and "~r~disable~w~" or "~g~enable~w~") 514 | end 515 | 516 | AddTextComponentInteger(radioConfig.Frequency.Current) 517 | EndTextCommandDisplayHelp(false, false, false, -1) 518 | 519 | -- Play animation if player is broadcasting to radio 520 | if Radio.On then 521 | if isBroadcasting and not isPlayingBroadcastAnim then 522 | RequestAnimDict(broadcastDictionary) 523 | 524 | while not HasAnimDictLoaded(broadcastDictionary) do 525 | Citizen.Wait(150) 526 | end 527 | 528 | TaskPlayAnim(playerPed, broadcastDictionary, broadcastAnimation, 8.0, -8, -1, 49, 0, 0, 0, 0) 529 | elseif not isBroadcasting and isPlayingBroadcastAnim then 530 | StopAnimTask(playerPed, broadcastDictionary, broadcastAnimation, -4.0) 531 | end 532 | end 533 | 534 | -- Play default animation if not broadcasting 535 | if not isBroadcasting and not isPlayingOpenAnim then 536 | RequestAnimDict(openDictionary) 537 | 538 | while not HasAnimDictLoaded(openDictionary) do 539 | Citizen.Wait(150) 540 | end 541 | 542 | TaskPlayAnim(playerPed, openDictionary, openAnimation, 4.0, -1, -1, 50, 0, false, false, false) 543 | end 544 | 545 | -- Turn radio on/off 546 | if IsControlJustPressed(0, radioConfig.Controls.Toggle.Key) then 547 | Radio.On = not Radio.On 548 | 549 | exports["pma-voice"]:setVoiceProperty("radioEnabled", Radio.On) 550 | 551 | if Radio.On then 552 | SendNUIMessage({ sound = "audio_on", volume = 0.3}) 553 | Radio:Add(radioConfig.Frequency.Current) 554 | else 555 | SendNUIMessage({ sound = "audio_off", volume = 0.5}) 556 | Radio:Remove() 557 | end 558 | end 559 | 560 | -- Change radio frequency 561 | if not Radio.On then 562 | DisableControlAction(0, radioConfig.Controls.ToggleClicks.Key, false) 563 | 564 | if not radioConfig.Controls.Decrease.Pressed then 565 | if IsControlJustPressed(0, radioConfig.Controls.Decrease.Key) then 566 | radioConfig.Controls.Decrease.Pressed = true 567 | Citizen.CreateThread(function() 568 | while IsControlPressed(0, radioConfig.Controls.Decrease.Key) do 569 | Radio:Decrease() 570 | Citizen.Wait(125) 571 | end 572 | 573 | radioConfig.Controls.Decrease.Pressed = false 574 | end) 575 | end 576 | end 577 | 578 | if not radioConfig.Controls.Increase.Pressed then 579 | if IsControlJustPressed(0, radioConfig.Controls.Increase.Key) then 580 | radioConfig.Controls.Increase.Pressed = true 581 | Citizen.CreateThread(function() 582 | while IsControlPressed(0, radioConfig.Controls.Increase.Key) do 583 | Radio:Increase() 584 | Citizen.Wait(125) 585 | end 586 | 587 | radioConfig.Controls.Increase.Pressed = false 588 | end) 589 | end 590 | end 591 | 592 | if not radioConfig.Controls.Input.Pressed then 593 | if IsControlJustPressed(0, radioConfig.Controls.Input.Key) then 594 | radioConfig.Controls.Input.Pressed = true 595 | Citizen.CreateThread(function() 596 | DisplayOnscreenKeyboard(1, Radio.Labels[3][1], "", radioConfig.Frequency.Current, "", "", "", 3) 597 | 598 | while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do 599 | Citizen.Wait(150) 600 | end 601 | 602 | local input = nil 603 | 604 | if UpdateOnscreenKeyboard() ~= 2 then 605 | input = GetOnscreenKeyboardResult() 606 | end 607 | 608 | Citizen.Wait(500) 609 | 610 | input = tonumber(input) 611 | 612 | if input ~= nil then 613 | if input >= minFrequency and input <= radioConfig.Frequency.List[#radioConfig.Frequency.List] and input == math.floor(input) then 614 | if not radioConfig.Frequency.Private[input] or radioConfig.Frequency.Access[input] then 615 | local idx = nil 616 | 617 | for i = 1, #radioConfig.Frequency.List do 618 | if radioConfig.Frequency.List[i] == input then 619 | idx = i 620 | break 621 | end 622 | end 623 | 624 | if idx ~= nil then 625 | radioConfig.Frequency.CurrentIndex = idx 626 | radioConfig.Frequency.Current = input 627 | end 628 | end 629 | end 630 | end 631 | 632 | radioConfig.Controls.Input.Pressed = false 633 | end) 634 | end 635 | end 636 | 637 | -- Turn radio mic clicks on/off 638 | if IsDisabledControlJustPressed(0, radioConfig.Controls.ToggleClicks.Key) then 639 | Radio.Clicks = not Radio.Clicks 640 | 641 | SendNUIMessage({ sound = "audio_off", volume = 0.5}) 642 | 643 | exports["pma-voice"]:setVoiceProperty("micClicks", Radio.Clicks) 644 | end 645 | end 646 | else 647 | -- Play emergency services radio animation 648 | if radioConfig.AllowRadioWhenClosed then 649 | if Radio.Has and Radio.On and isBroadcasting and not isPlayingBroadcastAnim then 650 | RequestAnimDict(broadcastDictionary) 651 | 652 | while not HasAnimDictLoaded(broadcastDictionary) do 653 | Citizen.Wait(150) 654 | end 655 | 656 | TaskPlayAnim(playerPed, broadcastDictionary, broadcastAnimation, 8.0, 0.0, -1, 49, 0, 0, 0, 0) 657 | elseif not isBroadcasting and isPlayingBroadcastAnim then 658 | StopAnimTask(playerPed, broadcastDictionary, broadcastAnimation, -4.0) 659 | end 660 | end 661 | end 662 | end 663 | end) 664 | 665 | AddEventHandler("onClientResourceStart", function(resName) 666 | if GetCurrentResourceName() ~= resName and "pma-voice" ~= resName then 667 | return 668 | end 669 | 670 | exports["pma-voice"]:setVoiceProperty("radioEnabled", false) -- Disable radio control 671 | 672 | if Radio.Open then 673 | Radio:Toggle(false) 674 | end 675 | 676 | Radio.On = false 677 | end) 678 | 679 | RegisterNetEvent("Radio.Toggle") 680 | AddEventHandler("Radio.Toggle", function() 681 | local playerPed = PlayerPedId() 682 | local isFalling = IsPedFalling(playerPed) 683 | local isDead = IsEntityDead(playerPed) 684 | 685 | if not isFalling and not isDead and Radio.Enabled and Radio.Has then 686 | Radio:Toggle(not Radio.Open) 687 | end 688 | end) 689 | 690 | RegisterNetEvent("Radio.Set") 691 | AddEventHandler("Radio.Set", function(value) 692 | if type(value) == "string" then 693 | value = value == "true" 694 | elseif type(value) == "number" then 695 | value = value == 1 696 | end 697 | 698 | Radio.Has = value and true or false 699 | end) 700 | --------------------------------------------------------------------------------