From 28bd2e797e3c257131cc9e7d6961a15d5d07e5e4 Mon Sep 17 00:00:00 2001 From: antho Date: Fri, 3 Oct 2025 23:12:10 +0200 Subject: [PATCH 1/2] Policejob major update: - Refactored client/main.lua (FreezePlayer, PlayerPedId, threads cleanup) - Refactored server/main.lua (secure buyWeapon/buyVehicle, account choice, MySQL checks) - Added Config.ArmoryAccount (money/bank) for purchases - Improved shared/permissions.lua and constants.lua handling - Fixed weapon purchase error and cleaned redundant code --- [esx_addons]/esx_policejob/client/main.lua | 2634 ++++++++--------- [esx_addons]/esx_policejob/client/vehicle.lua | 644 ++-- [esx_addons]/esx_policejob/fxmanifest.lua | 9 +- [esx_addons]/esx_policejob/server/main.lua | 812 ++--- .../esx_policejob/{ => shared}/config.lua | 9 +- .../esx_policejob/shared/constants.lua | 5 + .../esx_policejob/shared/permissions.lua | 17 + 7 files changed, 2081 insertions(+), 2049 deletions(-) rename [esx_addons]/esx_policejob/{ => shared}/config.lua (96%) create mode 100644 [esx_addons]/esx_policejob/shared/constants.lua create mode 100644 [esx_addons]/esx_policejob/shared/permissions.lua diff --git a/[esx_addons]/esx_policejob/client/main.lua b/[esx_addons]/esx_policejob/client/main.lua index efdfe61d..2c5fd98e 100644 --- a/[esx_addons]/esx_policejob/client/main.lua +++ b/[esx_addons]/esx_policejob/client/main.lua @@ -1,495 +1,483 @@ local CurrentActionData, handcuffTimer, dragStatus, blipsCops, currentTask = {}, {}, {}, {}, {} local HasAlreadyEnteredMarker, isDead, isHandcuffed, hasAlreadyJoined, playerInService = false, false, false, false, false local LastStation, LastPart, LastPartNum, LastEntity, CurrentAction, CurrentActionMsg +local ARREST_DICT, ARREST_ANIM = 'mp_arresting', 'idle' dragStatus.isDragged, isInShopMenu = false, false +local function playCuffAnim(ped) + RequestAnimDict(ARREST_DICT) + local tries = 0 + while not HasAnimDictLoaded(ARREST_DICT) and tries < 100 do + Wait(10); tries = tries + 1 + end + TaskPlayAnim(ped, ARREST_DICT, ARREST_ANIM, 8.0, -8.0, -1, 49, 0.0, false, false, false) +end +local function stopCuffAnim(ped) + ClearPedSecondaryTask(ped) + if HasAnimDictLoaded(ARREST_DICT) then + RemoveAnimDict(ARREST_DICT) + end +end + function cleanPlayer(playerPed) - SetPedArmour(playerPed, 0) - ClearPedBloodDamage(playerPed) - ResetPedVisibleDamage(playerPed) - ClearPedLastWeaponDamage(playerPed) - ResetPedMovementClipset(playerPed, 0) + SetPedArmour(playerPed, 0) + ClearPedBloodDamage(playerPed) + ResetPedVisibleDamage(playerPed) + ClearPedLastWeaponDamage(playerPed) + ResetPedMovementClipset(playerPed, 0) end function setUniform(uniform, playerPed) - TriggerEvent('skinchanger:getSkin', function(skin) - local uniformObject - - sex = (skin.sex == 0) and "male" or "female" - - uniformObject = Config.Uniforms[uniform][sex] - - if uniformObject then - TriggerEvent('skinchanger:loadClothes', skin, uniformObject) - - if uniform == 'bullet_wear' then - SetPedArmour(playerPed, 100) - end - else - ESX.ShowNotification(TranslateCap('no_outfit')) - end - end) + TriggerEvent('skinchanger:getSkin', function(skin) + local uniformObject + local sex = (skin.sex == 0) and "male" or "female" + + if Config.Uniforms and Config.Uniforms[uniform] and Config.Uniforms[uniform][sex] then + uniformObject = Config.Uniforms[uniform][sex] + end + + if uniformObject then + TriggerEvent('skinchanger:loadClothes', skin, uniformObject) + if uniform == 'bullet_wear' then + SetPedArmour(playerPed, 100) + end + else + ESX.ShowNotification(TranslateCap('no_outfit')) + end + end) end function OpenCloakroomMenu() - local playerPed = PlayerPedId() - local grade = ESX.PlayerData.job.grade_name - - local elements = { - {unselectable = true, icon = "fas fa-shirt", title = TranslateCap("cloakroom")}, - {icon = "fas fa-shirt", title = TranslateCap('citizen_wear'), value = 'citizen_wear'}, - {icon = "fas fa-shirt", title = TranslateCap('bullet_wear'), uniform = 'bullet_wear'}, - {icon = "fas fa-shirt", title = TranslateCap('gilet_wear'), uniform = 'gilet_wear'}, - {icon = "fas fa-shirt", title = TranslateCap('police_wear'), uniform = grade} - } - - if Config.EnableCustomPeds then - for k,v in ipairs(Config.CustomPeds.shared) do - elements[#elements+1] = { - icon = "fas fa-shirt", - title = v.label, - value = 'freemode_ped', - maleModel = v.maleModel, - femaleModel = v.femaleModel - } - end - - for k,v in ipairs(Config.CustomPeds[grade]) do - elements[#elements+1] = { - icon = "fas fa-shirt", - title = v.label, - value = 'freemode_ped', - maleModel = v.maleModel, - femaleModel = v.femaleModel - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - cleanPlayer(playerPed) - local data = {current = element} - - if data.current.value == 'citizen_wear' then - if Config.EnableCustomPeds then - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin) - local isMale = skin.sex == 0 - - TriggerEvent('skinchanger:loadDefaultModel', isMale, function() - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) - TriggerEvent('skinchanger:loadSkin', skin) - TriggerEvent('esx:restoreLoadout') - end) - end) - - end) - else - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) - TriggerEvent('skinchanger:loadSkin', skin) - end) - end - - if Config.EnableESXService then - ESX.TriggerServerCallback('esx_service:isInService', function(isInService) - if isInService then - playerInService = false - - local notification = { - title = TranslateCap('service_anonunce'), - subject = '', - msg = TranslateCap('service_out_announce', GetPlayerName(PlayerId())), - iconType = 1 - } - - TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') - - TriggerServerEvent('esx_service:disableService', 'police') - ESX.ShowNotification(TranslateCap('service_out')) - end - end, 'police') - end - end - - if Config.EnableESXService and data.current.value ~= 'citizen_wear' then - local awaitService - - ESX.TriggerServerCallback('esx_service:isInService', function(isInService) - if not isInService then - - if Config.MaxInService ~= -1 then - ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount) - if not canTakeService then - ESX.ShowNotification(TranslateCap('service_max', inServiceCount, maxInService)) - else - awaitService = true - playerInService = true - - local notification = { - title = TranslateCap('service_anonunce'), - subject = '', - msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), - iconType = 1 - } - - TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') - ESX.ShowNotification(TranslateCap('service_in')) - end - end, 'police') - else - awaitService = true - playerInService = true - - local notification = { - title = TranslateCap('service_anonunce'), - subject = '', - msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), - iconType = 1 - } - - TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') - ESX.ShowNotification(TranslateCap('service_in')) - end - - else - awaitService = true - end - end, 'police') - - while awaitService == nil do - Wait(0) - end - - -- if we couldn't enter service don't let the player get changed - if not awaitService then - return - end - end - - if data.current.uniform then - setUniform(data.current.uniform, playerPed) - elseif data.current.value == 'freemode_ped' then - local modelHash - - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin) - if skin.sex == 0 then - modelHash = joaat(data.current.maleModel) - else - modelHash = joaat(data.current.femaleModel) - end - - ESX.Streaming.RequestModel(modelHash, function() - SetPlayerModel(PlayerId(), modelHash) - SetModelAsNoLongerNeeded(modelHash) - SetPedDefaultComponentVariation(PlayerPedId()) - - TriggerEvent('esx:restoreLoadout') - end) - end) - end - end, function(menu) - CurrentAction = 'menu_cloakroom' - CurrentActionMsg = TranslateCap('open_cloackroom') - CurrentActionData = {} - end) + local playerPed = PlayerPedId() + local grade = ESX.PlayerData.job.grade_name + + local elements = { + {unselectable = true, icon = "fas fa-shirt", title = TranslateCap("cloakroom")}, + {icon = "fas fa-shirt", title = TranslateCap('citizen_wear'), value = 'citizen_wear'}, + {icon = "fas fa-shirt", title = TranslateCap('bullet_wear'), uniform = 'bullet_wear'}, + {icon = "fas fa-shirt", title = TranslateCap('gilet_wear'), uniform = 'gilet_wear'}, + {icon = "fas fa-shirt", title = TranslateCap('police_wear'), uniform = grade} + } + + if Config.EnableCustomPeds then + if Config.CustomPeds and Config.CustomPeds.shared then + for _,v in ipairs(Config.CustomPeds.shared) do + elements[#elements+1] = { + icon = "fas fa-shirt", + title = v.label, + value = 'freemode_ped', + maleModel = v.maleModel, + femaleModel = v.femaleModel + } + end + end + if Config.CustomPeds and Config.CustomPeds[grade] then + for _,v in ipairs(Config.CustomPeds[grade]) do + elements[#elements+1] = { + icon = "fas fa-shirt", + title = v.label, + value = 'freemode_ped', + maleModel = v.maleModel, + femaleModel = v.femaleModel + } + end + end + end + + ESX.OpenContext("right", elements, function(menu,element) + cleanPlayer(playerPed) + local data = {current = element} + + if data.current.value == 'citizen_wear' then + if Config.EnableCustomPeds then + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) + local isMale = skin.sex == 0 + TriggerEvent('skinchanger:loadDefaultModel', isMale, function() + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin2) + TriggerEvent('skinchanger:loadSkin', skin2) + TriggerEvent('esx:restoreLoadout') + end) + end) + end) + else + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) + TriggerEvent('skinchanger:loadSkin', skin) + end) + end + + if Config.EnableESXService then + ESX.TriggerServerCallback('esx_service:isInService', function(isInService) + if isInService then + playerInService = false + local notification = { + title = TranslateCap('service_anonunce'), + subject = '', + msg = TranslateCap('service_out_announce', GetPlayerName(PlayerId())), + iconType = 1 + } + TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') + TriggerServerEvent('esx_service:disableService', 'police') + ESX.ShowNotification(TranslateCap('service_out')) + end + end, 'police') + end + end + + if Config.EnableESXService and data.current.value ~= 'citizen_wear' then + local awaitService + ESX.TriggerServerCallback('esx_service:isInService', function(isInService) + if not isInService then + if Config.MaxInService ~= -1 then + ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount) + if not canTakeService then + ESX.ShowNotification(TranslateCap('service_max', inServiceCount, maxInService)) + else + awaitService = true + playerInService = true + local notification = { + title = TranslateCap('service_anonunce'), + subject = '', + msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), + iconType = 1 + } + TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') + ESX.ShowNotification(TranslateCap('service_in')) + end + end, 'police') + else + awaitService = true + playerInService = true + local notification = { + title = TranslateCap('service_anonunce'), + subject = '', + msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), + iconType = 1 + } + TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') + ESX.ShowNotification(TranslateCap('service_in')) + end + else + awaitService = true + end + end, 'police') + + while awaitService == nil do + Wait(0) + end + if not awaitService then + return + end + end + + if data.current.uniform then + setUniform(data.current.uniform, playerPed) + elseif data.current.value == 'freemode_ped' then + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) + local modelHash = skin.sex == 0 and joaat(data.current.maleModel) or joaat(data.current.femaleModel) + ESX.Streaming.RequestModel(modelHash, function() + SetPlayerModel(PlayerId(), modelHash) + SetModelAsNoLongerNeeded(modelHash) + SetPedDefaultComponentVariation(PlayerPedId()) + TriggerEvent('esx:restoreLoadout') + end) + end) + end + end, function(menu) + CurrentAction = 'menu_cloakroom' + CurrentActionMsg = TranslateCap('open_cloackroom') + CurrentActionData = {} + end) end function OpenArmoryMenu(station) - local elements - if Config.OxInventory then - exports.ox_inventory:openInventory('stash', {id = 'society_police', owner = station}) - return ESX.CloseContext() - else - elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory')}, - {icon = "fas fa-gun", title = TranslateCap('buy_weapons'), value = 'buy_weapons'} - - } - - if Config.EnableArmoryManagement then - table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('get_weapon'), value = 'get_weapon'}) - table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('put_weapon'), value = 'put_weapon'}) - table.insert(elements, {icon = "fas fa-box", title = TranslateCap('remove_object'), value = 'get_stock'}) - table.insert(elements, {icon = "fas fa-box", title = TranslateCap('deposit_object'), value = 'put_stock'}) - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if data.current.value == 'get_weapon' then - OpenGetWeaponMenu() - elseif data.current.value == 'put_weapon' then - OpenPutWeaponMenu() - elseif data.current.value == 'buy_weapons' then - OpenBuyWeaponsMenu() - elseif data.current.value == 'put_stock' then - OpenPutStocksMenu() - elseif data.current.value == 'get_stock' then - OpenGetStocksMenu() - end - end, function(menu) - CurrentAction = 'menu_armory' - CurrentActionMsg = TranslateCap('open_armory') - CurrentActionData = {station = station} - end) + if Config.OxInventory then + exports.ox_inventory:openInventory('stash', { id = 'society_police', owner = station }) + return ESX.CloseContext() + end + + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory')}, + {icon = "fas fa-gun", title = TranslateCap('buy_weapons'), value = 'buy_weapons'} + } + + if Config.EnableArmoryManagement then + table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('get_weapon'), value = 'get_weapon'}) + table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('put_weapon'), value = 'put_weapon'}) + table.insert(elements, {icon = "fas fa-box", title = TranslateCap('remove_object'), value = 'get_stock'}) + table.insert(elements, {icon = "fas fa-box", title = TranslateCap('deposit_object'), value = 'put_stock'}) + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + if data.current.value == 'get_weapon' then + OpenGetWeaponMenu() + elseif data.current.value == 'put_weapon' then + OpenPutWeaponMenu() + elseif data.current.value == 'buy_weapons' then + OpenBuyWeaponsMenu() + elseif data.current.value == 'put_stock' then + OpenPutStocksMenu() + elseif data.current.value == 'get_stock' then + OpenGetStocksMenu() + end + end, function(menu) + CurrentAction = 'menu_armory' + CurrentActionMsg = TranslateCap('open_armory') + CurrentActionData = {station = station} + end) end function OpenPoliceActionsMenu() - local elements = { - {unselectable = true, icon = "fas fa-police", title = TranslateCap('menu_title')}, - {icon = "fas fa-user", title = TranslateCap('citizen_interaction'), value = 'citizen_interaction'}, - {icon = "fas fa-car", title = TranslateCap('vehicle_interaction'), value = 'vehicle_interaction'}, - {icon = "fas fa-object", title = TranslateCap('object_spawner'), value = 'object_spawner'} - } - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - - if data.current.value == 'citizen_interaction' then - local elements2 = { - {unselectable = true, icon = "fas fa-user", title = element.title}, - {icon = "fas fa-idkyet", title = TranslateCap('id_card'), value = 'identity_card'}, - {icon = "fas fa-idkyet", title = TranslateCap('search'), value = 'search'}, - {icon = "fas fa-idkyet", title = TranslateCap('handcuff'), value = 'handcuff'}, - {icon = "fas fa-idkyet", title = TranslateCap('drag'), value = 'drag'}, - {icon = "fas fa-idkyet", title = TranslateCap('put_in_vehicle'), value = 'put_in_vehicle'}, - {icon = "fas fa-idkyet", title = TranslateCap('out_the_vehicle'), value = 'out_the_vehicle'}, - {icon = "fas fa-idkyet", title = TranslateCap('fine'), value = 'fine'}, - {icon = "fas fa-idkyet", title = TranslateCap('unpaid_bills'), value = 'unpaid_bills'} - } - - if Config.EnableLicenses then - elements2[#elements2+1] = { - icon = "fas fa-scroll", - title = TranslateCap('license_check'), - value = 'license' - } - end - - ESX.OpenContext("right", elements2, function(menu2,element2) - local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() - if closestPlayer ~= -1 and closestDistance <= 3.0 then - local data2 = {current = element2} - local action = data2.current.value - - if action == 'identity_card' then - OpenIdentityCardMenu(closestPlayer) - elseif action == 'search' then - OpenBodySearchMenu(closestPlayer) - elseif action == 'handcuff' then - TriggerServerEvent('esx_policejob:handcuff', GetPlayerServerId(closestPlayer)) - elseif action == 'drag' then - TriggerServerEvent('esx_policejob:drag', GetPlayerServerId(closestPlayer)) - elseif action == 'put_in_vehicle' then - TriggerServerEvent('esx_policejob:putInVehicle', GetPlayerServerId(closestPlayer)) - elseif action == 'out_the_vehicle' then - TriggerServerEvent('esx_policejob:OutVehicle', GetPlayerServerId(closestPlayer)) - elseif action == 'fine' then - OpenFineMenu(closestPlayer) - elseif action == 'license' then - ShowPlayerLicense(closestPlayer) - elseif action == 'unpaid_bills' then - OpenUnpaidBillsMenu(closestPlayer) - end - else - ESX.ShowNotification(TranslateCap('no_players_nearby')) - end - end, function(menu) - OpenPoliceActionsMenu() - end) - elseif data.current.value == 'vehicle_interaction' then - local elements3 = { - {unselectable = true, icon = "fas fa-car", title = element.title} - } - local playerPed = PlayerPedId() - local vehicle = ESX.Game.GetVehicleInDirection() - - if DoesEntityExist(vehicle) then - elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('vehicle_info'), value = 'vehicle_infos'} - elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('pick_lock'), value = 'hijack_vehicle'} - elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('impound'), value = 'impound'} - end - - elements3[#elements3+1] = { - icon = "fas fa-scroll", - title = TranslateCap('search_database'), - value = 'search_database' - } - - ESX.OpenContext("right", elements3, function(menu3,element3) - local data2 = {current = element3} - local coords = GetEntityCoords(playerPed) - vehicle = ESX.Game.GetVehicleInDirection() - action = data2.current.value - - if action == 'search_database' then - LookupVehicle(element3) - elseif DoesEntityExist(vehicle) then - if action == 'vehicle_infos' then - local vehicleData = ESX.Game.GetVehicleProperties(vehicle) - OpenVehicleInfosMenu(vehicleData) - elseif action == 'hijack_vehicle' then - if IsAnyVehicleNearPoint(coords.x, coords.y, coords.z, 3.0) then - TaskStartScenarioInPlace(playerPed, 'WORLD_HUMAN_WELDING', 0, true) - Wait(20000) - ClearPedTasksImmediately(playerPed) - - SetVehicleDoorsLocked(vehicle, 1) - SetVehicleDoorsLockedForAllPlayers(vehicle, false) - ESX.ShowNotification(TranslateCap('vehicle_unlocked')) - end - elseif action == 'impound' then - if currentTask.busy then - return - end - - ESX.ShowHelpNotification(TranslateCap('impound_prompt')) - TaskStartScenarioInPlace(playerPed, 'CODE_HUMAN_MEDIC_TEND_TO_DEAD', 0, true) - - currentTask.busy = true - currentTask.task = ESX.SetTimeout(10000, function() - ClearPedTasks(playerPed) - ImpoundVehicle(vehicle) - Wait(100) - end) - - CreateThread(function() - while currentTask.busy do - Wait(1000) - - vehicle = GetClosestVehicle(coords.x, coords.y, coords.z, 3.0, 0, 71) - if not DoesEntityExist(vehicle) and currentTask.busy then - ESX.ShowNotification(TranslateCap('impound_canceled_moved')) - ESX.ClearTimeout(currentTask.task) - ClearPedTasks(playerPed) - currentTask.busy = false - break - end - end - end) - end - else - ESX.ShowNotification(TranslateCap('no_vehicles_nearby')) - end - end, function(menu) - OpenPoliceActionsMenu() - end) - elseif data.current.value == "object_spawner" then - local elements4 = { - {unselectable = true, icon = "fas fa-object", title = element.title}, - {icon = "fas fa-cone", title = TranslateCap('cone'), model = 'prop_roadcone02a'}, - {icon = "fas fa-cone", title = TranslateCap('barrier'), model = 'prop_barrier_work05'}, - {icon = "fas fa-cone", title = TranslateCap('spikestrips'), model = 'p_ld_stinger_s'}, - {icon = "fas fa-cone", title = TranslateCap('box'), model = 'prop_boxpile_07d'}, - {icon = "fas fa-cone", title = TranslateCap('cash'), model = 'hei_prop_cash_crate_half_full'} - } - - ESX.OpenContext("right", elements4, function(menu4,element4) - local data2 = {current = element4} - local playerPed = PlayerPedId() - local coords, forward = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed) - local objectCoords = (coords + forward * 1.0) - - ESX.Game.SpawnObject(data2.current.model, objectCoords, function(obj) - Wait(100) - SetEntityHeading(obj, GetEntityHeading(playerPed)) - PlaceObjectOnGroundProperly(obj) - end) - end, function(menu) - OpenPoliceActionsMenu() - end) - end - end) + local elements = { + {unselectable = true, icon = "fas fa-police", title = TranslateCap('menu_title')}, + {icon = "fas fa-user", title = TranslateCap('citizen_interaction'), value = 'citizen_interaction'}, + {icon = "fas fa-car", title = TranslateCap('vehicle_interaction'), value = 'vehicle_interaction'}, + {icon = "fas fa-object", title = TranslateCap('object_spawner'), value = 'object_spawner'} + } + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + + if data.current.value == 'citizen_interaction' then + local elements2 = { + {unselectable = true, icon = "fas fa-user", title = element.title}, + {icon = "fas fa-idkyet", title = TranslateCap('id_card'), value = 'identity_card'}, + {icon = "fas fa-idkyet", title = TranslateCap('search'), value = 'search'}, + {icon = "fas fa-idkyet", title = TranslateCap('handcuff'), value = 'handcuff'}, + {icon = "fas fa-idkyet", title = TranslateCap('drag'), value = 'drag'}, + {icon = "fas fa-idkyet", title = TranslateCap('put_in_vehicle'), value = 'put_in_vehicle'}, + {icon = "fas fa-idkyet", title = TranslateCap('out_the_vehicle'), value = 'out_the_vehicle'}, + {icon = "fas fa-idkyet", title = TranslateCap('fine'), value = 'fine'}, + {icon = "fas fa-idkyet", title = TranslateCap('unpaid_bills'), value = 'unpaid_bills'} + } + + if Config.EnableLicenses then + elements2[#elements2+1] = { + icon = "fas fa-scroll", + title = TranslateCap('license_check'), + value = 'license' + } + end + + ESX.OpenContext("right", elements2, function(menu2,element2) + local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() + if closestPlayer ~= -1 and closestDistance <= 3.0 then + local data2 = {current = element2} + local action = data2.current.value + + if action == 'identity_card' then + OpenIdentityCardMenu(closestPlayer) + elseif action == 'search' then + OpenBodySearchMenu(closestPlayer) + elseif action == 'handcuff' then + TriggerServerEvent('esx_policejob:handcuff', GetPlayerServerId(closestPlayer)) + elseif action == 'drag' then + TriggerServerEvent('esx_policejob:drag', GetPlayerServerId(closestPlayer)) + elseif action == 'put_in_vehicle' then + TriggerServerEvent('esx_policejob:putInVehicle', GetPlayerServerId(closestPlayer)) + elseif action == 'out_the_vehicle' then + TriggerServerEvent('esx_policejob:OutVehicle', GetPlayerServerId(closestPlayer)) + elseif action == 'fine' then + OpenFineMenu(closestPlayer) + elseif action == 'license' then + ShowPlayerLicense(closestPlayer) + elseif action == 'unpaid_bills' then + OpenUnpaidBillsMenu(closestPlayer) + end + else + ESX.ShowNotification(TranslateCap('no_players_nearby')) + end + end, function(menu) + OpenPoliceActionsMenu() + end) + + elseif data.current.value == 'vehicle_interaction' then + local elements3 = { + {unselectable = true, icon = "fas fa-car", title = element.title} + } + local playerPed = PlayerPedId() + local vehicle = ESX.Game.GetVehicleInDirection() + + if DoesEntityExist(vehicle) then + elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('vehicle_info'), value = 'vehicle_infos'} + elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('pick_lock'), value = 'hijack_vehicle'} + elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('impound'), value = 'impound'} + end + + elements3[#elements3+1] = { + icon = "fas fa-scroll", + title = TranslateCap('search_database'), + value = 'search_database' + } + + ESX.OpenContext("right", elements3, function(menu3,element3) + local data2 = {current = element3} + local coords = GetEntityCoords(playerPed) + vehicle = ESX.Game.GetVehicleInDirection() + local action = data2.current.value + + if action == 'search_database' then + LookupVehicle(element3) + elseif DoesEntityExist(vehicle) then + if action == 'vehicle_infos' then + local vehicleData = ESX.Game.GetVehicleProperties(vehicle) + OpenVehicleInfosMenu(vehicleData) + elseif action == 'hijack_vehicle' then + if IsAnyVehicleNearPoint(coords.x, coords.y, coords.z, 3.0) then + TaskStartScenarioInPlace(playerPed, 'WORLD_HUMAN_WELDING', 0, true) + Wait(20000) + ClearPedTasksImmediately(playerPed) + SetVehicleDoorsLocked(vehicle, 1) + SetVehicleDoorsLockedForAllPlayers(vehicle, false) + ESX.ShowNotification(TranslateCap('vehicle_unlocked')) + end + elseif action == 'impound' then + if currentTask.busy then return end + ESX.ShowHelpNotification(TranslateCap('impound_prompt')) + TaskStartScenarioInPlace(playerPed, 'CODE_HUMAN_MEDIC_TEND_TO_DEAD', 0, true) + currentTask.busy = true + currentTask.task = ESX.SetTimeout(10000, function() + ClearPedTasks(playerPed) + ImpoundVehicle(vehicle) + Wait(100) + end) + CreateThread(function() + while currentTask.busy do + Wait(1000) + vehicle = GetClosestVehicle(coords.x, coords.y, coords.z, 3.0, 0, 71) + if not DoesEntityExist(vehicle) and currentTask.busy then + ESX.ShowNotification(TranslateCap('impound_canceled_moved')) + ESX.ClearTimeout(currentTask.task) + ClearPedTasks(playerPed) + currentTask.busy = false + break + end + end + end) + end + else + ESX.ShowNotification(TranslateCap('no_vehicles_nearby')) + end + end, function(menu) + OpenPoliceActionsMenu() + end) + + elseif data.current.value == "object_spawner" then + local elements4 = { + {unselectable = true, icon = "fas fa-object", title = element.title}, + {icon = "fas fa-cone", title = TranslateCap('cone'), model = 'prop_roadcone02a'}, + {icon = "fas fa-cone", title = TranslateCap('barrier'), model = 'prop_barrier_work05'}, + {icon = "fas fa-cone", title = TranslateCap('spikestrips'), model = 'p_ld_stinger_s'}, + {icon = "fas fa-cone", title = TranslateCap('box'), model = 'prop_boxpile_07d'}, + {icon = "fas fa-cone", title = TranslateCap('cash'), model = 'hei_prop_cash_crate_half_full'} + } + + ESX.OpenContext("right", elements4, function(menu4,element4) + local data2 = {current = element4} + local playerPed = PlayerPedId() + local coords, forward = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed) + local objectCoords = (coords + forward * 1.0) + ESX.Game.SpawnObject(data2.current.model, objectCoords, function(obj) + Wait(100) + SetEntityHeading(obj, GetEntityHeading(playerPed)) + PlaceObjectOnGroundProperly(obj) + end) + end, function(menu) + OpenPoliceActionsMenu() + end) + end + end) end function OpenIdentityCardMenu(player) - ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) - local elements = { - {icon = "fas fa-user", title = TranslateCap('name', data.name)}, - {icon = "fas fa-user", title = TranslateCap('job', ('%s - %s'):format(data.job, data.grade))} - } - - if Config.EnableESXIdentity then - elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('sex', TranslateCap(data.sex))} - elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('dob', data.dob)} - elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('height', data.height)} - end - - if Config.EnableESXOptionalneeds and data.drunk then - elements[#elements+1] = {title = TranslateCap('bac', data.drunk)} - end - - if data.licenses then - elements[#elements+1] = {title = TranslateCap('license_label')} - - for i=1, #data.licenses, 1 do - elements[#elements+1] = {title = data.licenses[i].label} - end - end - - ESX.OpenContext("right", elements, nil, function(menu) - OpenPoliceActionsMenu() - end) - end, GetPlayerServerId(player)) + ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) + local elements = { + {icon = "fas fa-user", title = TranslateCap('name', data.name)}, + {icon = "fas fa-user", title = TranslateCap('job', ('%s - %s'):format(data.job, data.grade))} + } + + if Config.EnableESXIdentity then + elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('sex', TranslateCap(data.sex))} + elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('dob', data.dob)} + elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('height', data.height)} + end + + if Config.EnableESXOptionalneeds and data.drunk then + elements[#elements+1] = {icon = "fas fa-wine-bottle", title = TranslateCap('bac', data.drunk)} + end + + if data.licenses then + elements[#elements+1] = {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_label')} + for i=1, #data.licenses do + elements[#elements+1] = {icon = "fas fa-scroll", title = data.licenses[i].label} + end + end + + ESX.OpenContext("right", elements, nil, function(menu) + OpenPoliceActionsMenu() + end) + end, GetPlayerServerId(player)) end function OpenBodySearchMenu(player) - if Config.OxInventory then - ESX.CloseContext() - exports.ox_inventory:openInventory('player', GetPlayerServerId(player)) - return - end - - ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) - local elements = { - {unselectable = true, icon = "fas fa-user", title = TranslateCap('search')} - } - - for i=1, #data.accounts, 1 do - if data.accounts[i].name == 'black_money' and data.accounts[i].money > 0 then - elements[#elements+1] = { - icon = "fas fa-money", - title = TranslateCap('confiscate_dirty', ESX.Math.Round(data.accounts[i].money)), - value = 'black_money', - itemType = 'item_account', - amount = data.accounts[i].money - } - break - end - end - - table.insert(elements, {label = TranslateCap('guns_label')}) - - for i=1, #data.weapons, 1 do - elements[#elements+1] = { - icon = "fas fa-gun", - title = TranslateCap('confiscate_weapon', ESX.GetWeaponLabel(data.weapons[i].name), data.weapons[i].ammo), - value = data.weapons[i].name, - itemType = 'item_weapon', - amount = data.weapons[i].ammo - } - end - - elements[#elements+1] = {title = TranslateCap('inventory_label')} - - for i=1, #data.inventory, 1 do - if data.inventory[i].count > 0 then - elements[#elements+1] = { - icon = "fas fa-box", - title = TranslateCap('confiscate_inv', data.inventory[i].count, data.inventory[i].label), - value = data.inventory[i].name, - itemType = 'item_standard', - amount = data.inventory[i].count - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if data.current.value then - TriggerServerEvent('esx_policejob:confiscatePlayerItem', GetPlayerServerId(player), data.current.itemType, data.current.value, data.current.amount) - OpenBodySearchMenu(player) - end - end) - end, GetPlayerServerId(player)) + if Config.OxInventory then + ESX.CloseContext() + exports.ox_inventory:openInventory('player', GetPlayerServerId(player)) + return + end + + ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) + local elements = { + {unselectable = true, icon = "fas fa-user", title = TranslateCap('search')} + } + + for i=1, #data.accounts do + if data.accounts[i].name == 'black_money' and data.accounts[i].money > 0 then + elements[#elements+1] = { + icon = "fas fa-money-bill", + title = TranslateCap('confiscate_dirty', ESX.Math.Round(data.accounts[i].money)), + value = 'black_money', + itemType = 'item_account', + amount = data.accounts[i].money + } + break + end + end + + elements[#elements+1] = {unselectable = true, icon = "fas fa-gun", title = TranslateCap('guns_label')} + + for i=1, #data.weapons do + elements[#elements+1] = { + icon = "fas fa-gun", + title = TranslateCap('confiscate_weapon', ESX.GetWeaponLabel(data.weapons[i].name), data.weapons[i].ammo), + value = data.weapons[i].name, + itemType = 'item_weapon', + amount = data.weapons[i].ammo + } + end + + elements[#elements+1] = {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory_label')} + + for i=1, #data.inventory do + if data.inventory[i].count > 0 then + elements[#elements+1] = { + icon = "fas fa-box", + title = TranslateCap('confiscate_inv', data.inventory[i].count, data.inventory[i].label), + value = data.inventory[i].name, + itemType = 'item_standard', + amount = data.inventory[i].count + } + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local dataEl = {current = element} + if dataEl.current.value then + TriggerServerEvent('esx_policejob:confiscatePlayerItem', GetPlayerServerId(player), dataEl.current.itemType, dataEl.current.value, dataEl.current.amount) + OpenBodySearchMenu(player) + end + end) + end, GetPlayerServerId(player)) end function OpenFineMenu(player) @@ -501,13 +489,11 @@ function OpenFineMenu(player) {icon = "fas fa-scroll", title = TranslateCap('average_offense'), value = 2}, {icon = "fas fa-scroll", title = TranslateCap('major_offense'), value = 3} } - ESX.OpenContext("right", elements, function(menu,element) local data = {current = element} OpenFineCategoryMenu(player, data.current.value) end) else - ESX.CloseContext() ESX.CloseContext() OpenFineTextInput(player) end @@ -516,41 +502,39 @@ end local fineList = {} function OpenFineCategoryMenu(player, category) if not fineList[category] then - local p = promise.new() - - ESX.TriggerServerCallback('esx_policejob:getFineList', function(fines) - p:resolve(fines) - end, category) + local p = promise.new() + ESX.TriggerServerCallback('esx_policejob:getFineList', function(fines) + p:resolve(fines) + end, category) + fineList[category] = Citizen.Await(p) + end - fineList[category] = Citizen.Await(p) + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('fine')} + } + + for _,fine in ipairs(fineList[category]) do + elements[#elements+1] = { + icon = "fas fa-scroll", + title = ('%s %s'):format(fine.label, TranslateCap('armory_item', ESX.Math.GroupDigits(fine.amount))), + value = fine.id, + amount = fine.amount, + fineLabel = fine.label + } end - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('fine')} - } - - for k,fine in ipairs(fineList[category]) do - elements[#elements+1] = { - icon = "fas fa-scroll", - title = ('%s %s'):format(fine.label, TranslateCap('armory_item', ESX.Math.GroupDigits(fine.amount))), - value = fine.id, - amount = fine.amount, - fineLabel = fine.label - } - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if Config.EnablePlayerManagement then - TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) - else - TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), '', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) - end - - ESX.SetTimeout(300, function() - OpenFineCategoryMenu(player, category) - end) - end) + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + if Config.EnablePlayerManagement then + TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) + else + TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), '', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) + end + + ESX.SetTimeout(300, function() + OpenFineCategoryMenu(player, category) + end) + end) end function OpenFineTextInput(player) @@ -585,964 +569,902 @@ function OpenFineTextInput(player) end) end - function LookupVehicle(elementF) - local elements = { - {unselectable = true, icon = "fas fa-car", title = elementF.title}, - {title = TranslateCap('search_plate'), input = true, inputType = "text", inputPlaceholder = "ABC 123"}, - {icon = "fas fa-check-double", title = TranslateCap('lookup_plate'), value = "lookup"} - } - - ESX.OpenContext("right", elements, function(menu,element) - local data = {value = menu.eles[2].inputValue} - local length = string.len(data.value) - if not data.value or length < 2 or length > 8 then - ESX.ShowNotification(TranslateCap('search_database_error_invalid')) - else - ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) - local elements = { - {unselectable = true, icon = "fas fa-car", title = element.title}, - {unselectable = true, icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} - } - - if not retrivedInfo.owner then - elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} - else - elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} - end - - ESX.OpenContext("right", elements, nil, function(menu) - OpenPoliceActionsMenu() - end) - end, data.value) - end - end) + local elements = { + {unselectable = true, icon = "fas fa-car", title = elementF.title}, + {title = TranslateCap('search_plate'), input = true, inputType = "text", inputPlaceholder = "ABC 123"}, + {icon = "fas fa-check-double", title = TranslateCap('lookup_plate'), value = "lookup"} + } + + ESX.OpenContext("right", elements, function(menu,element) + local data = {value = menu.eles[2].inputValue} + local length = data.value and string.len(data.value) or 0 + if not data.value or length < 2 or length > 8 then + ESX.ShowNotification(TranslateCap('search_database_error_invalid')) + else + ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) + local el = { + {unselectable = true, icon = "fas fa-car", title = elementF.title}, + {unselectable = true, icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} + } + if not retrivedInfo.owner then + el[#el+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} + else + el[#el+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} + end + ESX.OpenContext("right", el, nil, function() + OpenPoliceActionsMenu() + end) + end, data.value) + end + end) end function ShowPlayerLicense(player) - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_revoke')} - } - - ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(playerData) - if playerData.licenses then - for i=1, #playerData.licenses, 1 do - if playerData.licenses[i].label and playerData.licenses[i].type then - elements[#elements+1] = { - icon = "fas fa-scroll", - title = playerData.licenses[i].label, - type = playerData.licenses[i].type - } - end - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - ESX.ShowNotification(TranslateCap('licence_you_revoked', data.current.label, playerData.name)) - TriggerServerEvent('esx_policejob:message', GetPlayerServerId(player), TranslateCap('license_revoked', data.current.label)) - - TriggerServerEvent('esx_license:removeLicense', GetPlayerServerId(player), data.current.type) - - ESX.SetTimeout(300, function() - ShowPlayerLicense(player) - end) - end) - end, GetPlayerServerId(player)) + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_revoke')} + } + + ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(playerData) + if playerData.licenses then + for i=1, #playerData.licenses do + local lic = playerData.licenses[i] + if lic.label and lic.type then + elements[#elements+1] = { + icon = "fas fa-scroll", + title = lic.label, + type = lic.type + } + end + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + ESX.ShowNotification(TranslateCap('licence_you_revoked', data.current.title, playerData.name)) + TriggerServerEvent('esx_policejob:message', GetPlayerServerId(player), TranslateCap('license_revoked', data.current.title)) + TriggerServerEvent('esx_license:removeLicense', GetPlayerServerId(player), data.current.type) + ESX.SetTimeout(300, function() + ShowPlayerLicense(player) + end) + end) + end, GetPlayerServerId(player)) end function OpenUnpaidBillsMenu(player) - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('unpaid_bills')} - } - - ESX.TriggerServerCallback('esx_billing:getTargetBills', function(bills) - for k,bill in ipairs(bills) do - elements[#elements+1] = { - unselectable = true, - icon = "fas fa-scroll", - title = ('%s - %s'):format(bill.label, TranslateCap('armory_item', ESX.Math.GroupDigits(bill.amount))), - billId = bill.id - } - end - - ESX.OpenContext("right", elements, nil, nil) - - end, GetPlayerServerId(player)) + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('unpaid_bills')} + } + + ESX.TriggerServerCallback('esx_billing:getTargetBills', function(bills) + for _,bill in ipairs(bills) do + elements[#elements+1] = { + unselectable = true, + icon = "fas fa-scroll", + title = ('%s - %s'):format(bill.label, TranslateCap('armory_item', ESX.Math.GroupDigits(bill.amount))), + billId = bill.id + } + end + ESX.OpenContext("right", elements, nil, nil) + end, GetPlayerServerId(player)) end function OpenVehicleInfosMenu(vehicleData) - ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) - local elements = { - {unselectable = true, icon = "fas fa-car", title = TranslateCap('vehicle_info')}, - {icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} - - } - - if not retrivedInfo.owner then - elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} - else - elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} - end - - ESX.OpenContext("right", elements, nil, nil) - end, vehicleData.plate) + ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) + local elements = { + {unselectable = true, icon = "fas fa-car", title = TranslateCap('vehicle_info')}, + {icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} + } + if not retrivedInfo.owner then + elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} + else + elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} + end + ESX.OpenContext("right", elements, nil, nil) + end, vehicleData.plate) end function OpenGetWeaponMenu() - ESX.TriggerServerCallback('esx_policejob:getArmoryWeapons', function(weapons) - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('get_weapon_menu')} - } - - for i=1, #weapons, 1 do - if weapons[i].count > 0 then - elements[#elements+1] = { - icon = "fas fa-gun", - title = 'x' .. weapons[i].count .. ' ' .. ESX.GetWeaponLabel(weapons[i].name), - value = weapons[i].name - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - ESX.TriggerServerCallback('esx_policejob:removeArmoryWeapon', function() - ESX.CloseContext() - OpenGetWeaponMenu() - end, data.current.value) - end) - end) + ESX.TriggerServerCallback('esx_policejob:getArmoryWeapons', function(weapons) + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('get_weapon_menu')} + } + for i=1, #weapons do + if weapons[i].count > 0 then + elements[#elements+1] = { + icon = "fas fa-gun", + title = 'x' .. weapons[i].count .. ' ' .. ESX.GetWeaponLabel(weapons[i].name), + value = weapons[i].name + } + end + end + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + ESX.TriggerServerCallback('esx_policejob:removeArmoryWeapon', function() + ESX.CloseContext() + OpenGetWeaponMenu() + end, data.current.value) + end) + end) end function OpenPutWeaponMenu() - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('put_weapon_menu')} - } - local playerPed = PlayerPedId() - local weaponList = ESX.GetWeaponList() - - for i=1, #weaponList, 1 do - local weaponHash = joaat(weaponList[i].name) - - if HasPedGotWeapon(playerPed, weaponHash, false) and weaponList[i].name ~= 'WEAPON_UNARMED' then - elements[#elements+1] = { - icon = "fas fa-gun", - title = weaponList[i].label, - value = weaponList[i].name - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - ESX.TriggerServerCallback('esx_policejob:addArmoryWeapon', function() - ESX.CloseContext() - OpenPutWeaponMenu() - end, data.current.value, true) - end) + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('put_weapon_menu')} + } + local playerPed = PlayerPedId() + local weaponList = ESX.GetWeaponList() + + for i=1, #weaponList do + local weaponHash = joaat(weaponList[i].name) + if HasPedGotWeapon(playerPed, weaponHash, false) and weaponList[i].name ~= 'WEAPON_UNARMED' then + elements[#elements+1] = { + icon = "fas fa-gun", + title = weaponList[i].label, + value = weaponList[i].name + } + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + ESX.TriggerServerCallback('esx_policejob:addArmoryWeapon', function() + ESX.CloseContext() + OpenPutWeaponMenu() + end, data.current.value, true) + end) end function OpenBuyWeaponsMenu() - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory_weapontitle')} - } - local playerPed = PlayerPedId() - - for k,v in ipairs(Config.AuthorizedWeapons[ESX.PlayerData.job.grade_name]) do - local weaponNum, weapon = ESX.GetWeapon(v.weapon) - local components, label = {} - local hasWeapon = HasPedGotWeapon(playerPed, joaat(v.weapon), false) - - if v.components then - for i=1, #v.components do - if v.components[i] then - local component = weapon.components[i] - local hasComponent = HasPedGotWeaponComponent(playerPed, joaat(v.weapon), component.hash) - - if hasComponent then - label = ('%s: %s'):format(component.label, TranslateCap('armory_owned')) - else - if v.components[i] > 0 then - label = ('%s: %s'):format(component.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.components[i]))) - else - label = ('%s: %s'):format(component.label, TranslateCap('armory_free')) - end - end - - components[#components+1] = { - icon = "fas fa-gun", - title = label, - componentLabel = component.label, - hash = component.hash, - name = component.name, - price = v.components[i], - hasComponent = hasComponent, - componentNum = i - } - end - end - end - - if hasWeapon and v.components then - label = ('%s: >'):format(weapon.label) - elseif hasWeapon and not v.components then - label = ('%s: %s'):format(weapon.label, TranslateCap('armory_owned')) - else - if v.price > 0 then - label = ('%s: %s'):format(weapon.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.price))) - else - label = ('%s: %s'):format(weapon.label, TranslateCap('armory_free')) - end - end - - elements[#elements+1] = { - icon = "fas fa-gun", - title = label, - weaponLabel = weapon.label, - name = weapon.name, - components = components, - price = v.price, - hasWeapon = hasWeapon - } - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if data.current.hasWeapon then - if #data.current.components > 0 then - OpenWeaponComponentShop(data.current.components, data.current.name, menu) - end - else - ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) - if bought then - if data.current.price > 0 then - ESX.ShowNotification(TranslateCap('armory_bought', data.current.weaponLabel, ESX.Math.GroupDigits(data.current.price))) - end - - ESX.CloseContext() - OpenBuyWeaponsMenu() - else - ESX.ShowNotification(TranslateCap('armory_money')) - end - end, data.current.name, 1) - end - end) + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory_weapontitle')} + } + local playerPed = PlayerPedId() + + for _,v in ipairs(Config.AuthorizedWeapons[ESX.PlayerData.job.grade_name]) do + local weaponNum, weapon = ESX.GetWeapon(v.weapon) + local components, label = {}, '' + local hasWeapon = HasPedGotWeapon(playerPed, joaat(v.weapon), false) + + if v.components then + for i=1, #v.components do + if v.components[i] then + local component = weapon.components[i] + local hasComponent = HasPedGotWeaponComponent(playerPed, joaat(v.weapon), component.hash) + if hasComponent then + label = ('%s: %s'):format(component.label, TranslateCap('armory_owned')) + else + if v.components[i] > 0 then + label = ('%s: %s'):format(component.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.components[i]))) + else + label = ('%s: %s'):format(component.label, TranslateCap('armory_free')) + end + end + components[#components+1] = { + icon = "fas fa-gun", + title = label, + componentLabel = component.label, + hash = component.hash, + name = component.name, + price = v.components[i], + hasComponent = hasComponent, + componentNum = i + } + end + end + end + + if hasWeapon and v.components then + label = ('%s: '):format(weapon.label) + elseif hasWeapon and not v.components then + label = ('%s: %s'):format(weapon.label, TranslateCap('armory_owned')) + else + if v.price > 0 then + label = ('%s: %s'):format(weapon.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.price))) + else + label = ('%s: %s'):format(weapon.label, TranslateCap('armory_free')) + end + end + + elements[#elements+1] = { + icon = "fas fa-gun", + title = label, + weaponLabel = weapon.label, + name = weapon.name, + components = components, + price = v.price, + hasWeapon = hasWeapon + } + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + if data.current.hasWeapon then + if #data.current.components > 0 then + OpenWeaponComponentShop(data.current.components, data.current.name, menu) + end + else + ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) + if bought then + if data.current.price > 0 then + ESX.ShowNotification(TranslateCap('armory_bought', data.current.weaponLabel, ESX.Math.GroupDigits(data.current.price))) + end + ESX.CloseContext() + OpenBuyWeaponsMenu() + else + ESX.ShowNotification(TranslateCap('armory_money')) + end + end, data.current.name, 1) + end + end) end function OpenWeaponComponentShop(components, weaponName, parentShop) - - ESX.OpenContext("right", components, function(menu,element) - local data = {current = element} - if data.current.hasComponent then - ESX.ShowNotification(TranslateCap('armory_hascomponent')) - else - ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) - if bought then - if data.current.price > 0 then - ESX.ShowNotification(TranslateCap('armory_bought', data.current.componentLabel, ESX.Math.GroupDigits(data.current.price))) - end - - ESX.CloseContext() - parentShop.close() - OpenBuyWeaponsMenu() - else - ESX.ShowNotification(TranslateCap('armory_money')) - end - end, weaponName, 2, data.current.componentNum) - end - end) + ESX.OpenContext("right", components, function(menu,element) + local data = {current = element} + if data.current.hasComponent then + ESX.ShowNotification(TranslateCap('armory_hascomponent')) + else + ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) + if bought then + if data.current.price > 0 then + ESX.ShowNotification(TranslateCap('armory_bought', data.current.componentLabel, ESX.Math.GroupDigits(data.current.price))) + end + ESX.CloseContext() + parentShop.close() + OpenBuyWeaponsMenu() + else + ESX.ShowNotification(TranslateCap('armory_money')) + end + end, weaponName, 2, data.current.componentNum) + end + end) end function OpenGetStocksMenu() - ESX.TriggerServerCallback('esx_policejob:getStockItems', function(items) - local elements = { - {unselectable = true, icon = "fas fa-box", title = TranslateCap('police_stock')} - } - - for i=1, #items, 1 do - elements[#elements+1] = { - icon = "fas fa-box", - title = 'x' .. items[i].count .. ' ' .. items[i].label, - value = items[i].name - } - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - local itemName = data.current.value - - local elements2 = { - {unselectable = true, icon = "fas fa-box", title = element.title}, - {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, - {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} - } - - ESX.OpenContext("right", elements2, function(menu2,element2) - local data2 = {value = menu2.eles[2].inputValue} - local count = tonumber(data2.value) - - if not count then - ESX.ShowNotification(TranslateCap('quantity_invalid')) - else - ESX.CloseContext() - TriggerServerEvent('esx_policejob:getStockItem', itemName, count) - - Wait(300) - OpenGetStocksMenu() - end - end) - end) - end) + ESX.TriggerServerCallback('esx_policejob:getStockItems', function(items) + local elements = { + {unselectable = true, icon = "fas fa-box", title = TranslateCap('police_stock')} + } + for i=1, #items do + elements[#elements+1] = { + icon = "fas fa-box", + title = 'x' .. items[i].count .. ' ' .. items[i].label, + value = items[i].name + } + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + local itemName = data.current.value + + local elements2 = { + {unselectable = true, icon = "fas fa-box", title = element.title}, + {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, + {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} + } + + ESX.OpenContext("right", elements2, function(menu2,element2) + local data2 = {value = menu2.eles[2].inputValue} + local count = tonumber(data2.value) + if not count then + ESX.ShowNotification(TranslateCap('quantity_invalid')) + else + ESX.CloseContext() + TriggerServerEvent('esx_policejob:getStockItem', itemName, count) + Wait(300) + OpenGetStocksMenu() + end + end) + end) + end) end function OpenPutStocksMenu() - ESX.TriggerServerCallback('esx_policejob:getPlayerInventory', function(inventory) - local elements = { - {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory')} - } - - for i=1, #inventory.items, 1 do - local item = inventory.items[i] - - if item.count > 0 then - elements[#elements+1] = { - icon = "fas fa-box", - title = item.label .. ' x' .. item.count, - type = 'item_standard', - value = item.name - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - local itemName = data.current.value - - local elements2 = { - {unselectable = true, icon = "fas fa-box", title = element.title}, - {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, - {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} - } - - ESX.OpenContext("right", elements2, function(menu2,element2) - local data2 = {value = menu2.eles[2].inputValue} - local count = tonumber(data2.value) - - if not count then - ESX.ShowNotification(TranslateCap('quantity_invalid')) - else - ESX.CloseContext() - TriggerServerEvent('esx_policejob:putStockItems', itemName, count) - - Wait(300) - OpenPutStocksMenu() - end - end) - end) - end) + ESX.TriggerServerCallback('esx_policejob:getPlayerInventory', function(inventory) + local elements = { + {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory')} + } + for i=1, #inventory.items do + local item = inventory.items[i] + if item.count > 0 then + elements[#elements+1] = { + icon = "fas fa-box", + title = item.label .. ' x' .. item.count, + type = 'item_standard', + value = item.name + } + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + local itemName = data.current.value + + local elements2 = { + {unselectable = true, icon = "fas fa-box", title = element.title}, + {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, + {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} + } + + ESX.OpenContext("right", elements2, function(menu2,element2) + local data2 = {value = menu2.eles[2].inputValue} + local count = tonumber(data2.value) + if not count then + ESX.ShowNotification(TranslateCap('quantity_invalid')) + else + ESX.CloseContext() + TriggerServerEvent('esx_policejob:putStockItems', itemName, count) + Wait(300) + OpenPutStocksMenu() + end + end) + end) + end) end RegisterNetEvent('esx_phone:loaded') AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) - local specialContact = { - name = TranslateCap('phone_police'), - number = 'police', - base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDFGQTJDRkI0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDFGQTJDRkM0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0MUZBMkNGOTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0MUZBMkNGQTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoW66EYAAAjGSURBVHjapJcLcFTVGcd/u3cfSXaTLEk2j80TCI8ECI9ABCyoiBqhBVQqVG2ppVKBQqUVgUl5OU7HKqNOHUHU0oHamZZWoGkVS6cWAR2JPJuAQBPy2ISEvLN57+v2u2E33e4k6Ngz85+9d++95/zP9/h/39GpqsqiRYsIGz8QZAq28/8PRfC+4HT4fMXFxeiH+GC54NeCbYLLATLpYe/ECx4VnBTsF0wWhM6lXY8VbBE0Ch4IzLcpfDFD2P1TgrdC7nMCZLRxQ9AkiAkQCn77DcH3BC2COoFRkCSIG2JzLwqiQi0RSmCD4JXbmNKh0+kc/X19tLtc9Ll9sk9ZS1yoU71YIk3xsbEx8QaDEc2ttxmaJSKC1ggSKBK8MKwTFQVXRzs3WzpJGjmZgvxcMpMtWIwqsjztvSrlzjYul56jp+46qSmJmMwR+P3+4aZ8TtCprRkk0DvUW7JjmV6lsqoKW/pU1q9YQOE4Nxkx4ladE7zd8ivuVmJQfXZKW5dx5EwPRw4fxNx2g5SUVLw+33AkzoRaQDP9SkFu6OKqz0uF8yaz7vsOL6ycQVLkcSg/BlWNsjuFoKE1knqDSl5aNnmPLmThrE0UvXqQqvJPyMrMGorEHwQfEha57/3P7mXS684GFjy8kreLppPUuBXfyd/ibeoS2kb0mWPANhJdYjb61AxUvx5PdT3+4y+Tb3mTd19ZSebE+VTXVGNQlHAC7w4VhH8TbA36vKq6ilnzlvPSunHw6Trc7XpZ14AyfgYeyz18crGN1Alz6e3qwNNQSv4dZox1h/BW9+O7eIaEsVv41Y4XeHJDG83Nl4mLTwzGhJYtx0PzNTjOB9KMTlc7Nkcem39YAGU7cbeBKVLMPGMVf296nMd2VbBq1wmizHoqqm/wrS1/Zf0+N19YN2PIu1fcIda4Vk66Zx/rVi+jo9eIX9wZGGcFXUMR6BHUa76/2ezioYcXMtpyAl91DSaTfDxlJbtLprHm2ecpObqPuTPzSNV9yKz4a4zJSuLo71/j8Q17ON69EmXiPIlNMe6FoyzOqWPW/MU03Lw5EFcyKghTrNDh7+/vw545mcJcWbTiGKpRdGPMXbx90sGmDaux6sXk+kimjU+BjnMkx3kYP34cXrFuZ+3nrHi6iDMt92JITcPjk3R3naRwZhpuNSqoD93DKaFVU7j2dhcF8+YzNlpErbIBTVh8toVccbaysPB+4pMcuPw25kwSsau7BIlmHpy3guaOPtISYyi/UkaJM5Lpc5agq5Xkcl6gIHkmqaMn0dtylcjIyPThCNyhaXyfR2W0I1our0v6qBii07ih5rDtGSOxNVdk1y4R2SR8jR/g7hQD9l1jUeY/WLJB5m39AlZN4GZyIQ1fFJNsEgt0duBIc5GRkcZF53mNwIzhXPDgQPoZIkiMkbTxtstDMVnmFA4cOsbz2/aKjSQjev4Mp9ZAg+hIpFhB3EH5Yal16+X+Kq3dGfxkzRY+KauBjBzREvGN0kNCTARu94AejBLMHorAQ7cEQMGs2cXvkWshYLDi6e9l728O8P1XW6hKeB2yv42q18tjj+iFTGoSi+X9jJM9RTxS9E+OHT0krhNiZqlbqraoT7RAU5bBGrEknEBhgJks7KXbLS8qERI0ErVqF/Y4K6NHZfLZB+/wzJvncacvFd91oXO3o/O40MfZKJOKu/rne+mRQByXM4lYreb1tUnkizVVA/0SpfpbWaCNBeEE5gb/UH19NLqEgDF+oNDQWcn41Cj0EXFEWqzkOIyYekslFkThsvMxpIyE2hIc6lXGZ6cPyK7Nnk5OipixRdxgUESAYmhq68VsGgy5CYKCUAJTg0+izApXne3CJFmUTwg4L3FProFxU+6krqmXu3MskkhSD2av41jLdzlnfFrSdCZxyqfMnppN6ZUa7pwt0h3fiK9DCt4IO9e7YqisvI7VYgmNv7mhBKKD/9psNi5dOMv5ZjukjsLdr0ffWsyTi6eSlfcA+dmiVyOXs+/sHNZu3M6PdxzgVO9GmDSHsSNqmTz/R6y6Xxqma4fwaS5Mn85n1ZE0Vl3CHBER3lUNEhiURpPJRFdTOcVnpUJnPIhR7cZXfoH5UYc5+E4RzRH3sfSnl9m2dSMjE+Tz9msse+o5dr7UwcQ5T3HwlWUkNuzG3dKFSTbsNs7m/Y8vExOlC29UWkMJlAxKoRQMR3IC7x85zOn6fHS50+U/2Untx2R1voinu5no+DQmz7yPXmMKZnsu0wrm0Oe3YhOVHdm8A09dBQYhTv4T7C+xUPrZh8Qn2MMr4qcDSRfoirWgKAvtgOpv1JI8Zi77X15G7L+fxeOUOiUFxZiULD5fSlNzNM62W+k1yq5gjajGX/ZHvOIyxd+Fkj+P092rWP/si0Qr7VisMaEWuCiYonXFwbAUTWWPYLV245NITnGkUXnpI9butLJn2y6iba+hlp7C09qBcvoN7FYL9mhxo1/y/LoEXK8Pv6qIC8WbBY/xr9YlPLf9dZT+OqKTUwfmDBm/GOw7ws4FWpuUP2gJEZvKqmocuXPZuWYJMzKuSsH+SNwh3bo0p6hao6HeEqwYEZ2M6aKWd3PwTCy7du/D0F1DsmzE6/WGLr5LsDF4LggnYBacCOboQLHQ3FFfR58SR+HCR1iQH8ukhA5s5o5AYZMwUqOp74nl8xvRHDlRTsnxYpJsUjtsceHt2C8Fm0MPJrphTkZvBc4It9RKLOFx91Pf0Igu0k7W2MmkOewS2QYJUJVWVz9VNbXUVVwkyuAmKTFJayrDo/4Jwe/CT0aGYTrWVYEeUfsgXssMRcpyenraQJa0VX9O3ZU+Ma1fax4xGxUsUVFkOUbcama1hf+7+LmA9juHWshwmwOE1iMmCFYEzg1jtIm1BaxW6wCGGoFdewPfvyE4ertTiv4rHC73B855dwp2a23bbd4tC1hvhOCbX7b4VyUQKhxrtSOaYKngasizvwi0RmOS4O1QZf2yYfiaR+73AvhTQEVf+rpn9/8IMAChKDrDzfsdIQAAAABJRU5ErkJggg==' - } - - TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) + local specialContact = { + name = TranslateCap('phone_police'), + number = 'police', + base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDFGQTJDRkI0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDFGQTJDRkM0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0MUZBMkNGOTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0MUZBMkNGQTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoW66EYAAAjGSURBVHjapJcLcFTVGcd/u3cfSXaTLEk2j80TCI8ECI9ABCyoiBqhBVQqVG2ppVKBQqUVgUl5OU7HKqNOHUHU0oHamZZWoGkVS6cWAR2JPJuAQBPy2ISEvLN57+v2u2E33e4k6Ngz85+9d++95/zP9/h/39GpqsqiRYsIGz8QZAq28/8PRfC+4HT4fMXFxeiH+GC54NeCbYLLATLpYe/ECx4VnBTsF0wWhM6lXY8VbBE0Ch4IzLcpfDFD2P1TgrdC7nMCZLRxQ9AkiAkQCn77DcH3BC2COoFRkCSIG2JzLwqiQi0RSmCD4JXbmNKh0+kc/X19tLtc9Ll9sk9ZS1yoU71YIk3xsbEx8QaDEc2ttxmaJSKC1ggSKBK8MKwTFQVXRzs3WzpJGjmZgvxcMpMtWIwqsjztvSrlzjYul56jp+46qSmJmMwR+P3+4aZ8TtCprRkk0DvUW7JjmV6lsqoKW/pU1q9YQOE4Nxkx4ladE7zd8ivuVmJQfXZKW5dx5EwPRw4fxNx2g5SUVLw+33AkzoRaQDP9SkFu6OKqz0uF8yaz7vsOL6ycQVLkcSg/BlWNsjuFoKE1knqDSl5aNnmPLmThrE0UvXqQqvJPyMrMGorEHwQfEha57/3P7mXS684GFjy8kreLppPUuBXfyd/ibeoS2kb0mWPANhJdYjb61AxUvx5PdT3+4y+Tb3mTd19ZSebE+VTXVGNQlHAC7w4VhH8TbA36vKq6ilnzlvPSunHw6Trc7XpZ14AyfgYeyz18crGN1Alz6e3qwNNQSv4dZox1h/BW9+O7eIaEsVv41Y4XeHJDG83Nl4mLTwzGhJYtx0PzNTjOB9KMTlc7Nkcem39YAGU7cbeBKVLMPGMVf296nMd2VbBq1wmizHoqqm/wrS1/Zf0+N19YN2PIu1fcIda4Vk66Zx/rVi+jo9eIX9wZGGcFXUMR6BHUa76/2ezioYcXMtpyAl91DSaTfDxlJbtLprHm2ecpObqPuTPzSNV9yKz4a4zJSuLo71/j8Q17ON69EmXiPIlNMe6FoyzOqWPW/MU03Lw5EFcyKghTrNDh7+/vw545mcJcWbTiGKpRdGPMXbx90sGmDaux6sXk+kimjU+BjnMkx3kYP34cXrFuZ+3nrHi6iDMt92JITcPjk3R3naRwZhpuNSqoD93DKaFVU7j2dhcF8+YzNlpErbIBTVh8toVccbaysPB+4pMcuPw25kwSsau7BIlmHpy3guaOPtISYyi/UkaJM5Lpc5agq5Xkcl6gIHkmqaMn0dtylcjIyPThCNyhaXyfR2W0I1our0v6qBii07ih5rDtGSOxNVdk1y4R2SR8jR/g7hQD9l1jUeY/WLJB5m39AlZN4GZyIQ1fFJNsEgt0duBIc5GRkcZF53mNwIzhXPDgQPoZIkiMkbTxtstDMVnmFA4cOsbz2/aKjSQjev4Mp9ZAg+hIpFhB3EH5Yal16+X+Kq3dGfxkzRY+KauBjBzREvGN0kNCTARu94AejBLMHorAQ7cEQMGs2cXvkWshYLDi6e9l728O8P1XW6hKeB2yv42q18tjj+iFTGoSi+X9jJM9RTxS9E+OHT0krhNiZqlbqraoT7RAU5bBGrEknEBhgJks7KXbLS8qERI0ErVqF/Y4K6NHZfLZB+/wzJvncacvFd91oXO3o/O40MfZKJOKu/rne+mRQByXM4lYreb1tUnkizVVA/0SpfpbWaCNBeEE5gb/UH19NLqEgDF+oNDQWcn41Cj0EXFEWqzkOIyYekslFkThsvMxpIyE2hIc6lXGZ6cPyK7Nnk5OipixRdxgUESAYmhq68VsGgy5CYKCUAJTg0+izApXne3CJFmUTwg4L3FProFxU+6krqmXu3MskkhSD2av41jLdzlnfFrSdCZxyqfMnppN6ZUa7pwt0h3fiK9DCt4IO9e7YqisvI7VYgmNv7mhBKKD/9psNi5dOMv5ZjukjsLdr0ffWsyTi6eSlfcA+dmiVyOXs+/sHNZu3M6PdxzgVO9GmDSHsSNqmTz/R6y6Xxqma4fwaS5Mn85n1ZE0Vl3CHBER3lUNEhiURpPJRFdTOcVnpUJnPIhR7cZXfoH5UYc5+E4RzRH3sfSnl9m2dSMjE+Tz9msse+o5dr7UwcQ5T3HwlWUkNuzG3dKFSTbsNs7m/Y8vExOlC29UWkMJlAxKoRQMR3IC7x85zOn6fHS50+U/2Untx2R1voinu5no+DQmz7yPXmMKZnsu0wrm0Oe3YhOVHdm8A09dBQYhTv4T7C+xUPrZh8Qn2MMr4qcDSRfoirWgKAvtgOpv1JI8Zi77X15G7L+fxeOUOiUFxZiULD5fSlNzNM62W+k1yq5gjajGX/ZHvOIyxd+Fkj+P092rWP/si0Qr7VisMaEWuCiYonXFwbAUTWWPYLV245NITnGkUXnpI9butLJn2y6iba+hlp7C09qBcvoN7FYL9mhxo1/y/LoEXK8Pv6qIC8WbBY/xr9YlPLf9dZT+OqKTUwfmDBm/GOw7ws4FWpuUP2gJEZvKqmocuXPZuWYJMzKuSsH+SNwh3bo0p6hao6HeEqwYEZ2M6aKWd3PwTCy7du/D0F1DsmzE6/WGLr5LsDF4LggnYBacCOboQLHQ3FFfR58SR+HCR1iQH8ukhA5s5o5AYZMwUqOp74nl8xvRHDlRTsnxYpJsUjtsceHt2C8Fm0MPJrphTkZvBc4It9RKLOFx91Pf0Igu0k7W2MmkOewS2QYJUJVWVz9VNbXUVVwkyuAmKTFJayrDo/4Jwe/CT0aGYTrWVYEeUfsgXssMRcpyenraQJa0VX9O3ZU+Ma1fax4xGxUsUVFkOUbcama1hf+7+LmA9juHWshwmwOE1iMmCFYEzg1jtIm1BaxW6wCGGoFdewPfvyE4ertTiv4rHC73B855dwp2a23bbd4tC1hvhOCbX7b4VyUQKhxrtSOaYKngasizvwi0RmOS4O1QZf2yYfiaR+73AvhTQEVf+rpn9/8IMAChKDrDzfsdIQAAAABJRU5ErkJggg==' + } + TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) end) --- don't show dispatches if the player isn't in service AddEventHandler('esx_phone:cancelMessage', function(dispatchNumber) - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and ESX.PlayerData.job.name == dispatchNumber then - -- if esx_service is enabled - if Config.EnableESXService and not playerInService then - CancelEvent() - end - end + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and dispatchNumber == 'police' then + if Config.EnableESXService and not playerInService then + CancelEvent() + end + end end) AddEventHandler('esx_policejob:hasEnteredMarker', function(station, part, partNum) - if part == 'Cloakroom' then - CurrentAction = 'menu_cloakroom' - CurrentActionMsg = TranslateCap('open_cloackroom') - CurrentActionData = {} - elseif part == 'Armory' then - CurrentAction = 'menu_armory' - CurrentActionMsg = TranslateCap('open_armory') - CurrentActionData = {station = station} - elseif part == 'Vehicles' then - CurrentAction = 'menu_vehicle_spawner' - CurrentActionMsg = TranslateCap('garage_prompt') - CurrentActionData = {station = station, part = part, partNum = partNum} - elseif part == 'Helicopters' then - CurrentAction = 'Helicopters' - CurrentActionMsg = TranslateCap('helicopter_prompt') - CurrentActionData = {station = station, part = part, partNum = partNum} - elseif part == 'BossActions' then - CurrentAction = 'menu_boss_actions' - CurrentActionMsg = TranslateCap('open_bossmenu') - CurrentActionData = {} - end + if part == 'Cloakroom' then + CurrentAction = 'menu_cloakroom' + CurrentActionMsg = TranslateCap('open_cloackroom') + CurrentActionData = {} + elseif part == 'Armory' then + CurrentAction = 'menu_armory' + CurrentActionMsg = TranslateCap('open_armory') + CurrentActionData = {station = station} + elseif part == 'Vehicles' then + CurrentAction = 'menu_vehicle_spawner' + CurrentActionMsg = TranslateCap('garage_prompt') + CurrentActionData = {station = station, part = part, partNum = partNum} + elseif part == 'Helicopters' then + CurrentAction = 'Helicopters' + CurrentActionMsg = TranslateCap('helicopter_prompt') + CurrentActionData = {station = station, part = part, partNum = partNum} + elseif part == 'BossActions' then + CurrentAction = 'menu_boss_actions' + CurrentActionMsg = TranslateCap('open_bossmenu') + CurrentActionData = {} + end end) -AddEventHandler('esx_policejob:hasExitedMarker', function(station, part, partNum) - if not isInShopMenu then - ESX.CloseContext() - end - - CurrentAction = nil +AddEventHandler('esx_policejob:hasExitedMarker', function() + if not isInShopMenu then + ESX.CloseContext() + end + CurrentAction = nil end) AddEventHandler('esx_policejob:hasEnteredEntityZone', function(entity) - local playerPed = PlayerPedId() - - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and IsPedOnFoot(playerPed) then - CurrentAction = 'remove_entity' - CurrentActionMsg = TranslateCap('remove_prop') - CurrentActionData = {entity = entity} - end - - if GetEntityModel(entity) == `p_ld_stinger_s` then - local playerPed = PlayerPedId() - local coords = GetEntityCoords(playerPed) - - if IsPedInAnyVehicle(playerPed, false) then - local vehicle = GetVehiclePedIsIn(playerPed) - - for i=0, 7, 1 do - SetVehicleTyreBurst(vehicle, i, true, 1000) - end - end - end + local playerPed = PlayerPedId() + + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and IsPedOnFoot(playerPed) then + CurrentAction = 'remove_entity' + CurrentActionMsg = TranslateCap('remove_prop') + CurrentActionData = {entity = entity} + end + + if GetEntityModel(entity) == `p_ld_stinger_s` then + if IsPedInAnyVehicle(playerPed, false) then + local vehicle = GetVehiclePedIsIn(playerPed) + for i=0, 7 do + SetVehicleTyreBurst(vehicle, i, true, 1000) + end + end + end end) AddEventHandler('esx_policejob:hasExitedEntityZone', function(entity) - if CurrentAction == 'remove_entity' then - CurrentAction = nil - end + if CurrentAction == 'remove_entity' and CurrentActionData and CurrentActionData.entity == entity then + CurrentAction = nil + end end) RegisterNetEvent('esx_policejob:handcuff') AddEventHandler('esx_policejob:handcuff', function() - isHandcuffed = not isHandcuffed - local playerPed = PlayerPedId() - - if isHandcuffed then - RequestAnimDict('mp_arresting') - while not HasAnimDictLoaded('mp_arresting') do - Wait(100) - end - - TaskPlayAnim(playerPed, 'mp_arresting', 'idle', 8.0, -8, -1, 49, 0, 0, 0, 0) - RemoveAnimDict('mp_arresting') - - SetEnableHandcuffs(playerPed, true) - DisablePlayerFiring(playerPed, true) - SetCurrentPedWeapon(playerPed, `WEAPON_UNARMED`, true) -- unarm player - SetPedCanPlayGestureAnims(playerPed, false) - FreezeEntityPosition(playerPed, true) - DisplayRadar(false) - - if Config.EnableHandcuffTimer then - if handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - - StartHandcuffTimer() - end - else - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - - ClearPedSecondaryTask(playerPed) - SetEnableHandcuffs(playerPed, false) - DisablePlayerFiring(playerPed, false) - SetPedCanPlayGestureAnims(playerPed, true) - FreezeEntityPosition(playerPed, false) - DisplayRadar(true) - end + isHandcuffed = not isHandcuffed + local playerPed = PlayerPedId() + + if isHandcuffed then + playCuffAnim(playerPed) + + SetEnableHandcuffs(playerPed, true) + DisablePlayerFiring(playerPed, true) + SetCurrentPedWeapon(playerPed, `WEAPON_UNARMED`, true) + SetPedCanPlayGestureAnims(playerPed, false) + + if Config.Cuffs and Config.Cuffs.FreezePlayer then + FreezeEntityPosition(playerPed, true) + else + FreezeEntityPosition(playerPed, false) + SetPedCanRagdoll(playerPed, true) + SetPedMoveRateOverride(playerPed, 1.0) + SetRunSprintMultiplierForPlayer(PlayerId(), 1.0) + end + + DisplayRadar(false) + SetEntityCollision(playerPed, true, true) + SetEntityDynamic(playerPed, true) + TriggerServerEvent('esx_policejob:cuffsState', true) + + if Config.EnableHandcuffTimer then + if handcuffTimer.active then ESX.ClearTimeout(handcuffTimer.task) end + StartHandcuffTimer() + end + else + if Config.EnableHandcuffTimer and handcuffTimer.active then + ESX.ClearTimeout(handcuffTimer.task) + end + + stopCuffAnim(playerPed) + SetEnableHandcuffs(playerPed, false) + DisablePlayerFiring(playerPed, false) + SetPedCanPlayGestureAnims(playerPed, true) + FreezeEntityPosition(playerPed, false) + DisplayRadar(true) + SetEntityCollision(playerPed, true, true) + SetEntityDynamic(playerPed, true) + + TriggerServerEvent('esx_policejob:cuffsState', false) + end end) RegisterNetEvent('esx_policejob:unrestrain') AddEventHandler('esx_policejob:unrestrain', function() - if isHandcuffed then - local playerPed = PlayerPedId() - isHandcuffed = false - - ClearPedSecondaryTask(playerPed) - SetEnableHandcuffs(playerPed, false) - DisablePlayerFiring(playerPed, false) - SetPedCanPlayGestureAnims(playerPed, true) - FreezeEntityPosition(playerPed, false) - DisplayRadar(true) - - -- end timer - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - end + if not isHandcuffed then return end + local playerPed = PlayerPedId() + isHandcuffed = false + + stopCuffAnim(playerPed) + SetEnableHandcuffs(playerPed, false) + DisablePlayerFiring(playerPed, false) + SetPedCanPlayGestureAnims(playerPed, true) + FreezeEntityPosition(playerPed, false) + DisplayRadar(true) + + if Config.EnableHandcuffTimer and handcuffTimer.active then + ESX.ClearTimeout(handcuffTimer.task) + end + + TriggerServerEvent('esx_policejob:cuffsState', false) +end) + +CreateThread(function() + while true do + if isHandcuffed and (not Config.Cuffs or not Config.Cuffs.FreezePlayer) then + local ped = PlayerPedId() + + if IsPedUsingAnyScenario(ped) then + ClearPedTasksImmediately(ped) + end + + if IsEntityPlayingAnim(ped, ARREST_DICT, ARREST_ANIM, 3) ~= 1 then + playCuffAnim(ped) + end + + FreezeEntityPosition(ped, false) + + DisableControlAction(0, 24, true) -- Attack + DisableControlAction(0, 25, true) -- Aim + DisableControlAction(0, 37, true) -- Select Weapon + DisableControlAction(0, 140, true) -- Melee light + DisableControlAction(0, 141, true) -- Melee heavy + DisableControlAction(0, 142, true) -- Melee alt + DisableControlAction(0, 47, true) -- Weapon + DisableControlAction(2, 36, true) -- Stealth + Wait(0) + else + Wait(300) + end + end end) RegisterNetEvent('esx_policejob:drag') AddEventHandler('esx_policejob:drag', function(copId) - if isHandcuffed then - dragStatus.isDragged = not dragStatus.isDragged - dragStatus.CopId = copId - end + if isHandcuffed then + dragStatus.isDragged = not dragStatus.isDragged + dragStatus.CopId = copId + end end) CreateThread(function() - local wasDragged - - while true do - local Sleep = 1500 - - if isHandcuffed and dragStatus.isDragged then - Sleep = 50 - local targetPed = GetPlayerPed(GetPlayerFromServerId(dragStatus.CopId)) - - if DoesEntityExist(targetPed) and IsPedOnFoot(targetPed) and not Player(dragStatus.CopId).state.isDead then - if not wasDragged then - AttachEntityToEntity(ESX.PlayerData.ped, targetPed, 11816, 0.54, 0.54, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) - wasDragged = true - else - Wait(1000) - end - else - wasDragged = false - dragStatus.isDragged = false - DetachEntity(ESX.PlayerData.ped, true, false) - end - elseif wasDragged then - wasDragged = false - DetachEntity(ESX.PlayerData.ped, true, false) - end - Wait(Sleep) - end + local wasDragged + while true do + local sleep = 1500 + if isHandcuffed and dragStatus.isDragged then + sleep = 50 + local targetPed = GetPlayerPed(GetPlayerFromServerId(dragStatus.CopId)) + if DoesEntityExist(targetPed) and IsPedOnFoot(targetPed) and not Player(dragStatus.CopId).state.isDead then + if not wasDragged then + AttachEntityToEntity(PlayerPedId(), targetPed, 11816, 0.54, 0.54, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) + wasDragged = true + else + Wait(1000) + end + else + wasDragged = false + dragStatus.isDragged = false + DetachEntity(PlayerPedId(), true, false) + end + elseif wasDragged then + wasDragged = false + DetachEntity(PlayerPedId(), true, false) + end + Wait(sleep) + end end) RegisterNetEvent('esx_policejob:putInVehicle') AddEventHandler('esx_policejob:putInVehicle', function() - if isHandcuffed then - local playerPed = PlayerPedId() - local vehicle, distance = ESX.Game.GetClosestVehicle() - - if vehicle and distance < 5 then - local maxSeats, freeSeat = GetVehicleMaxNumberOfPassengers(vehicle) - - for i=maxSeats - 1, 0, -1 do - if IsVehicleSeatFree(vehicle, i) then - freeSeat = i - break - end - end - - if freeSeat then - TaskWarpPedIntoVehicle(playerPed, vehicle, freeSeat) - dragStatus.isDragged = false - end - end - end + if isHandcuffed then + local playerPed = PlayerPedId() + local vehicle, distance = ESX.Game.GetClosestVehicle() + if vehicle and distance < 5.0 then + local maxSeats, freeSeat = GetVehicleMaxNumberOfPassengers(vehicle) + for i=maxSeats - 1, 0, -1 do + if IsVehicleSeatFree(vehicle, i) then freeSeat = i break end + end + if freeSeat then + TaskWarpPedIntoVehicle(playerPed, vehicle, freeSeat) + dragStatus.isDragged = false + end + end + end end) RegisterNetEvent('esx_policejob:OutVehicle') AddEventHandler('esx_policejob:OutVehicle', function() - local GetVehiclePedIsIn = GetVehiclePedIsIn - local IsPedSittingInAnyVehicle = IsPedSittingInAnyVehicle - local TaskLeaveVehicle = TaskLeaveVehicle - if IsPedSittingInAnyVehicle(ESX.PlayerData.ped) then - local vehicle = GetVehiclePedIsIn(ESX.PlayerData.ped, false) - TaskLeaveVehicle(ESX.PlayerData.ped, vehicle, 64) - end + local ped = PlayerPedId() + if IsPedSittingInAnyVehicle(ped) then + local vehicle = GetVehiclePedIsIn(ped, false) + TaskLeaveVehicle(ped, vehicle, 64) + end end) --- Handcuff CreateThread(function() - local DisableControlAction = DisableControlAction - local IsEntityPlayingAnim = IsEntityPlayingAnim - while true do - local Sleep = 1000 - - if isHandcuffed then - Sleep = 0 - DisableControlAction(0, 1, true) -- Disable pan - DisableControlAction(0, 2, true) -- Disable tilt - DisableControlAction(0, 24, true) -- Attack - DisableControlAction(0, 257, true) -- Attack 2 - DisableControlAction(0, 25, true) -- Aim - DisableControlAction(0, 263, true) -- Melee Attack 1 - DisableControlAction(0, 32, true) -- W - DisableControlAction(0, 34, true) -- A - DisableControlAction(0, 31, true) -- S - DisableControlAction(0, 30, true) -- D - - DisableControlAction(0, 45, true) -- Reload - DisableControlAction(0, 22, true) -- Jump - DisableControlAction(0, 44, true) -- Cover - DisableControlAction(0, 37, true) -- Select Weapon - DisableControlAction(0, 23, true) -- Also 'enter'? - - DisableControlAction(0, 288, true) -- Disable phone - DisableControlAction(0, 289, true) -- Inventory - DisableControlAction(0, 170, true) -- Animations - DisableControlAction(0, 167, true) -- Job - - DisableControlAction(0, 0, true) -- Disable changing view - DisableControlAction(0, 26, true) -- Disable looking behind - DisableControlAction(0, 73, true) -- Disable clearing animation - DisableControlAction(2, 199, true) -- Disable pause screen - - DisableControlAction(0, 59, true) -- Disable steering in vehicle - DisableControlAction(0, 71, true) -- Disable driving forward in vehicle - DisableControlAction(0, 72, true) -- Disable reversing in vehicle - - DisableControlAction(2, 36, true) -- Disable going stealth - - DisableControlAction(0, 47, true) -- Disable weapon - DisableControlAction(0, 264, true) -- Disable melee - DisableControlAction(0, 257, true) -- Disable melee - DisableControlAction(0, 140, true) -- Disable melee - DisableControlAction(0, 141, true) -- Disable melee - DisableControlAction(0, 142, true) -- Disable melee - DisableControlAction(0, 143, true) -- Disable melee - DisableControlAction(0, 75, true) -- Disable exit vehicle - DisableControlAction(27, 75, true) -- Disable exit vehicle - - if IsEntityPlayingAnim(ESX.PlayerData.ped, 'mp_arresting', 'idle', 3) ~= 1 then - ESX.Streaming.RequestAnimDict('mp_arresting', function() - TaskPlayAnim(ESX.PlayerData.ped, 'mp_arresting', 'idle', 8.0, -8, -1, 49, 0.0, false, false, false) - RemoveAnimDict('mp_arresting') - end) - end - end - Wait(Sleep) - end + while true do + local sleep = 1000 + if isHandcuffed and Config.Cuffs and Config.Cuffs.FreezePlayer then + sleep = 0 + DisableControlAction(0, 1, true) -- Look L/R + DisableControlAction(0, 2, true) -- Look U/D + DisableControlAction(0, 24, true) -- Attack + DisableControlAction(0, 257, true) -- Attack2 + DisableControlAction(0, 25, true) -- Aim + DisableControlAction(0, 263, true) -- Melee + DisableControlAction(0, 32, true) -- W + DisableControlAction(0, 34, true) -- A + DisableControlAction(0, 31, true) -- S + DisableControlAction(0, 30, true) -- D + DisableControlAction(0, 45, true) -- Reload + DisableControlAction(0, 22, true) -- Jump + DisableControlAction(0, 44, true) -- Cover + DisableControlAction(0, 37, true) -- Select Weapon + DisableControlAction(0, 23, true) -- Enter + DisableControlAction(0, 288, true) -- Phone + DisableControlAction(0, 289, true) -- Inventory + DisableControlAction(0, 170, true) -- Animations + DisableControlAction(0, 167, true) -- Job + DisableControlAction(0, 0, true) -- View + DisableControlAction(0, 26, true) -- Look behind + DisableControlAction(0, 73, true) -- Clear anim + DisableControlAction(2, 199, true) -- Pause + DisableControlAction(0, 59, true) -- Steer in vehicle + DisableControlAction(0, 71, true) -- Forward in vehicle + DisableControlAction(0, 72, true) -- Reverse in vehicle + DisableControlAction(2, 36, true) -- Stealth + DisableControlAction(0, 47, true) -- Weapon + DisableControlAction(0, 264, true) + DisableControlAction(0, 257, true) + DisableControlAction(0, 140, true) + DisableControlAction(0, 141, true) + DisableControlAction(0, 142, true) + DisableControlAction(0, 143, true) + DisableControlAction(0, 75, true) -- Exit vehicle + DisableControlAction(27, 75, true) + +local ped = PlayerPedId() + if IsEntityPlayingAnim(ped, 'mp_arresting', 'idle', 3) ~= 1 then + ESX.Streaming.RequestAnimDict('mp_arresting', function() + TaskPlayAnim(ped, 'mp_arresting', 'idle', 8.0, -8.0, -1, 49, 0.0, false, false, false) + RemoveAnimDict('mp_arresting') + end) + end + end + Wait(sleep) + end end) --- Create blips CreateThread(function() - for k,v in pairs(Config.PoliceStations) do - local blip = AddBlipForCoord(v.Blip.Coords) - - SetBlipSprite (blip, v.Blip.Sprite) - SetBlipDisplay(blip, v.Blip.Display) - SetBlipScale (blip, v.Blip.Scale) - SetBlipColour (blip, v.Blip.Colour) - SetBlipAsShortRange(blip, true) - - BeginTextCommandSetBlipName('STRING') - AddTextComponentSubstringPlayerName(TranslateCap('map_blip')) - EndTextCommandSetBlipName(blip) - end + for _,v in pairs(Config.PoliceStations) do + local blip = AddBlipForCoord(v.Blip.Coords) + SetBlipSprite (blip, v.Blip.Sprite) + SetBlipDisplay(blip, v.Blip.Display) + SetBlipScale (blip, v.Blip.Scale) + SetBlipColour (blip, v.Blip.Colour) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName('STRING') + AddTextComponentSubstringPlayerName(TranslateCap('map_blip')) + EndTextCommandSetBlipName(blip) + end end) --- Draw markers and more CreateThread(function() - while true do - local Sleep = 1500 - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then - Sleep = 500 - local playerPed = PlayerPedId() - local playerCoords = GetEntityCoords(playerPed) - local isInMarker, hasExited = false, false - local currentStation, currentPart, currentPartNum - - for k,v in pairs(Config.PoliceStations) do - for i=1, #v.Cloakrooms, 1 do - local distance = #(playerCoords - v.Cloakrooms[i]) - - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Cloakrooms, v.Cloakrooms[i], 0.0, 0.0, 0.0, 0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Cloakroom', i - end - end - end - - for i=1, #v.Armories, 1 do - local distance = #(playerCoords - v.Armories[i]) - - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Armories, v.Armories[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Armory', i - end - end - end - - for i=1, #v.Vehicles, 1 do - local distance = #(playerCoords - v.Vehicles[i].Spawner) - - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Vehicles, v.Vehicles[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Vehicles', i - end - end - end - - for i=1, #v.Helicopters, 1 do - local distance = #(playerCoords - v.Helicopters[i].Spawner) - - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Helicopters, v.Helicopters[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Helicopters', i - end - end - end - - if Config.EnablePlayerManagement and ESX.PlayerData.job.grade_name == 'boss' then - for i=1, #v.BossActions, 1 do - local distance = #(playerCoords - v.BossActions[i]) - - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.BossActions, v.BossActions[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'BossActions', i - end - end - end - end - end - - if isInMarker and not HasAlreadyEnteredMarker or (isInMarker and (LastStation ~= currentStation or LastPart ~= currentPart or LastPartNum ~= currentPartNum)) then - if - (LastStation and LastPart and LastPartNum) and - (LastStation ~= currentStation or LastPart ~= currentPart or LastPartNum ~= currentPartNum) - then - TriggerEvent('esx_policejob:hasExitedMarker', LastStation, LastPart, LastPartNum) - hasExited = true - end - - HasAlreadyEnteredMarker = true - LastStation = currentStation - LastPart = currentPart - LastPartNum = currentPartNum - - TriggerEvent('esx_policejob:hasEnteredMarker', currentStation, currentPart, currentPartNum) - end - - if not hasExited and not isInMarker and HasAlreadyEnteredMarker then - HasAlreadyEnteredMarker = false - TriggerEvent('esx_policejob:hasExitedMarker', LastStation, LastPart, LastPartNum) - end - end - Wait(Sleep) - end + while true do + local Sleep = 1500 + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then + Sleep = 500 + local playerPed = PlayerPedId() + local playerCoords = GetEntityCoords(playerPed) + local isInMarker, hasExited = false, false + local currentStation, currentPart, currentPartNum + + for k,v in pairs(Config.PoliceStations) do + for i=1, #v.Cloakrooms do + local distance = #(playerCoords - v.Cloakrooms[i]) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Cloakrooms, v.Cloakrooms[i], 0.0, 0.0, 0.0, 0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Cloakroom', i + end + end + end + + for i=1, #v.Armories do + local distance = #(playerCoords - v.Armories[i]) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Armories, v.Armories[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Armory', i + end + end + end + + for i=1, #v.Vehicles do + local distance = #(playerCoords - v.Vehicles[i].Spawner) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Vehicles, v.Vehicles[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Vehicles', i + end + end + end + + for i=1, #v.Helicopters do + local distance = #(playerCoords - v.Helicopters[i].Spawner) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Helicopters, v.Helicopters[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Helicopters', i + end + end + end + + if Config.EnablePlayerManagement and ESX.PlayerData.job.grade_name == 'boss' then + for i=1, #v.BossActions do + local distance = #(playerCoords - v.BossActions[i]) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.BossActions, v.BossActions[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'BossActions', i + end + end + end + end + end + + if (isInMarker and not HasAlreadyEnteredMarker) or (isInMarker and (LastStation ~= currentStation or LastPart ~= currentPart or LastPartNum ~= currentPartNum)) then + if (LastStation and LastPart and LastPartNum) and (LastStation ~= currentStation or LastPart ~= currentPart or LastPartNum ~= currentPartNum) then + TriggerEvent('esx_policejob:hasExitedMarker', LastStation, LastPart, LastPartNum) + hasExited = true + end + HasAlreadyEnteredMarker = true + LastStation = currentStation + LastPart = currentPart + LastPartNum = currentPartNum + TriggerEvent('esx_policejob:hasEnteredMarker', currentStation, currentPart, currentPartNum) + end + + if not hasExited and not isInMarker and HasAlreadyEnteredMarker then + HasAlreadyEnteredMarker = false + TriggerEvent('esx_policejob:hasExitedMarker', LastStation, LastPart, LastPartNum) + end + end + Wait(Sleep) + end end) --- Enter / Exit entity zone events CreateThread(function() - local trackedEntities = { - `prop_roadcone02a`, - `prop_barrier_work05`, - `p_ld_stinger_s`, - `prop_boxpile_07d`, - `hei_prop_cash_crate_half_full` - } - - while true do - local Sleep = 1500 - - local GetEntityCoords = GetEntityCoords - local GetClosestObjectOfType = GetClosestObjectOfType - local DoesEntityExist = DoesEntityExist - local playerCoords = GetEntityCoords(ESX.PlayerData.ped) - - local closestDistance = -1 - local closestEntity = nil - - for i=1, #trackedEntities, 1 do - local object = GetClosestObjectOfType(playerCoords, 3.0, trackedEntities[i], false, false, false) - - if DoesEntityExist(object) then - Sleep = 500 - local objCoords = GetEntityCoords(object) - local distance = #(playerCoords - objCoords) - - if closestDistance == -1 or closestDistance > distance then - closestDistance = distance - closestEntity = object - end - end - end - - if closestDistance ~= -1 and closestDistance <= 3.0 then - if LastEntity ~= closestEntity then - TriggerEvent('esx_policejob:hasEnteredEntityZone', closestEntity) - LastEntity = closestEntity - end - else - if LastEntity then - TriggerEvent('esx_policejob:hasExitedEntityZone', LastEntity) - LastEntity = nil - end - end - Wait(Sleep) - end + local trackedEntities = { + `prop_roadcone02a`, + `prop_barrier_work05`, + `p_ld_stinger_s`, + `prop_boxpile_07d`, + `hei_prop_cash_crate_half_full` + } + + while true do + local Sleep = 1500 + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then + local playerCoords = GetEntityCoords(PlayerPedId()) + local closestDistance = -1 + local closestEntity = nil + + for i=1, #trackedEntities do + local object = GetClosestObjectOfType(playerCoords, 3.0, trackedEntities[i], false, false, false) + if DoesEntityExist(object) then + Sleep = 500 + local objCoords = GetEntityCoords(object) + local distance = #(playerCoords - objCoords) + if closestDistance == -1 or closestDistance > distance then + closestDistance = distance + closestEntity = object + end + end + end + + if closestDistance ~= -1 and closestDistance <= 3.0 then + if LastEntity ~= closestEntity then + TriggerEvent('esx_policejob:hasEnteredEntityZone', closestEntity) + LastEntity = closestEntity + end + else + if LastEntity then + TriggerEvent('esx_policejob:hasExitedEntityZone', LastEntity) + LastEntity = nil + end + end + end + Wait(Sleep) + end end) ESX.RegisterInput("police:interact", "(ESX PoliceJob) " .. TranslateCap('interaction'), "keyboard", "E", function() - if not CurrentAction then - return - end - - if not ESX.PlayerData.job or (ESX.PlayerData.job and not ESX.PlayerData.job.name == 'police') then - return - end - if CurrentAction == 'menu_cloakroom' then - OpenCloakroomMenu() - elseif CurrentAction == 'menu_armory' then - if not Config.EnableESXService then - OpenArmoryMenu(CurrentActionData.station) - elseif playerInService then - OpenArmoryMenu(CurrentActionData.station) - else - ESX.ShowNotification(TranslateCap('service_not')) - end - elseif CurrentAction == 'menu_vehicle_spawner' then - if not Config.EnableESXService then - OpenVehicleSpawnerMenu('car', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) - elseif playerInService then - OpenVehicleSpawnerMenu('car', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) - else - ESX.ShowNotification(TranslateCap('service_not')) - end - elseif CurrentAction == 'Helicopters' then - if not Config.EnableESXService then - OpenVehicleSpawnerMenu('helicopter', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) - elseif playerInService then - OpenVehicleSpawnerMenu('helicopter', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) - else - ESX.ShowNotification(TranslateCap('service_not')) - end - elseif CurrentAction == 'delete_vehicle' then - ESX.Game.DeleteVehicle(CurrentActionData.vehicle) - elseif CurrentAction == 'menu_boss_actions' then - ESX.CloseContext() - TriggerEvent('esx_society:openBossMenu', 'police', function(data, menu) - ESX.CloseContext() - - CurrentAction = 'menu_boss_actions' - CurrentActionMsg = TranslateCap('open_bossmenu') - CurrentActionData = {} - end, { wash = false }) -- disable washing money - elseif CurrentAction == 'remove_entity' then - DeleteEntity(CurrentActionData.entity) - end - - CurrentAction = nil + if not CurrentAction then return end + if (not ESX.PlayerData.job) or (ESX.PlayerData.job.name ~= 'police') then + return + end + + if CurrentAction == 'menu_cloakroom' then + OpenCloakroomMenu() + elseif CurrentAction == 'menu_armory' then + if not Config.EnableESXService or playerInService then + OpenArmoryMenu(CurrentActionData.station) + else + ESX.ShowNotification(TranslateCap('service_not')) + end + elseif CurrentAction == 'menu_vehicle_spawner' then + if not Config.EnableESXService or playerInService then + OpenVehicleSpawnerMenu('car', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) + else + ESX.ShowNotification(TranslateCap('service_not')) + end + elseif CurrentAction == 'Helicopters' then + if not Config.EnableESXService or playerInService then + OpenVehicleSpawnerMenu('helicopter', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) + else + ESX.ShowNotification(TranslateCap('service_not')) + end + elseif CurrentAction == 'delete_vehicle' then + ESX.Game.DeleteVehicle(CurrentActionData.vehicle) + elseif CurrentAction == 'menu_boss_actions' then + ESX.CloseContext() + TriggerEvent('esx_society:openBossMenu', 'police', function() + ESX.CloseContext() + CurrentAction = 'menu_boss_actions' + CurrentActionMsg = TranslateCap('open_bossmenu') + CurrentActionData = {} + end, { wash = false }) + elseif CurrentAction == 'remove_entity' then + if CurrentActionData and CurrentActionData.entity and DoesEntityExist(CurrentActionData.entity) then + DeleteEntity(CurrentActionData.entity) + end + end + + CurrentAction = nil end) ESX.RegisterInput("police:quickactions", "(ESX PoliceJob) "..TranslateCap('quick_actions'), "keyboard", "F6", function() - if not ESX.PlayerData.job or (ESX.PlayerData.job.name ~= 'police') or isDead then - return - end - - if not Config.EnableESXService then - OpenPoliceActionsMenu() - elseif playerInService then - OpenPoliceActionsMenu() - else - ESX.ShowNotification(TranslateCap('service_not')) - end + if not ESX.PlayerData.job or (ESX.PlayerData.job.name ~= 'police') or isDead then + return + end + if not Config.EnableESXService or playerInService then + OpenPoliceActionsMenu() + else + ESX.ShowNotification(TranslateCap('service_not')) + end end) CreateThread(function() - while true do - local Sleep = 1000 - - if CurrentAction then - Sleep = 0 - ESX.ShowHelpNotification(CurrentActionMsg) - end - Wait(Sleep) - end + while true do + local Sleep = 1000 + if CurrentAction then + Sleep = 0 + ESX.ShowHelpNotification(CurrentActionMsg) + end + Wait(Sleep) + end end) --- Create blip for colleagues function createBlip(id) - local ped = GetPlayerPed(id) - local blip = GetBlipFromEntity(ped) - - if not DoesBlipExist(blip) then -- Add blip and create head display on player - blip = AddBlipForEntity(ped) - SetBlipSprite(blip, 1) - ShowHeadingIndicatorOnBlip(blip, true) -- Player Blip indicator - SetBlipRotation(blip, math.ceil(GetEntityHeading(ped))) -- update rotation - SetBlipNameToPlayerName(blip, id) -- update blip name - SetBlipScale(blip, 0.85) -- set scale - SetBlipAsShortRange(blip, true) - - table.insert(blipsCops, blip) -- add blip to array so we can remove it later - end + local ped = GetPlayerPed(id) + local blip = GetBlipFromEntity(ped) + if not DoesBlipExist(blip) then + blip = AddBlipForEntity(ped) + SetBlipSprite(blip, 1) + ShowHeadingIndicatorOnBlip(blip, true) + SetBlipRotation(blip, math.ceil(GetEntityHeading(ped))) + SetBlipNameToPlayerName(blip, id) + SetBlipScale(blip, 0.85) + SetBlipAsShortRange(blip, true) + table.insert(blipsCops, blip) + end end -AddEventHandler('esx:onPlayerSpawn', function(spawn) - isDead = false - TriggerEvent('esx_policejob:unrestrain') - - if not hasAlreadyJoined then - TriggerServerEvent('esx_policejob:spawned') - end - hasAlreadyJoined = true +AddEventHandler('esx:onPlayerSpawn', function() + isDead = false + TriggerEvent('esx_policejob:unrestrain') + if not hasAlreadyJoined then + TriggerServerEvent('esx_policejob:spawned') + end + hasAlreadyJoined = true end) -AddEventHandler('esx:onPlayerDeath', function(data) - isDead = true +AddEventHandler('esx:onPlayerDeath', function() + isDead = true end) AddEventHandler('onResourceStop', function(resource) - if resource == GetCurrentResourceName() then - TriggerEvent('esx_policejob:unrestrain') - TriggerEvent('esx_phone:removeSpecialContact', 'police') - - if Config.EnableESXService then - TriggerServerEvent('esx_service:disableService', 'police') - end - - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - end -end) + if resource == GetCurrentResourceName() then + TriggerEvent('esx_policejob:unrestrain') + TriggerEvent('esx_phone:removeSpecialContact', 'police') --- handcuff timer, unrestrain the player after an certain amount of time -function StartHandcuffTimer() - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end + if Config.EnableESXService then + TriggerServerEvent('esx_service:disableService', 'police') + end - handcuffTimer.active = true + if Config.EnableHandcuffTimer and handcuffTimer.active then + ESX.ClearTimeout(handcuffTimer.task) + end + end +end) - handcuffTimer.task = ESX.SetTimeout(Config.HandcuffTimer, function() - ESX.ShowNotification(TranslateCap('unrestrained_timer')) - TriggerEvent('esx_policejob:unrestrain') - handcuffTimer.active = false - end) +function StartHandcuffTimer() + if Config.EnableHandcuffTimer and handcuffTimer.active then + ESX.ClearTimeout(handcuffTimer.task) + end + handcuffTimer.active = true + handcuffTimer.task = ESX.SetTimeout(Config.HandcuffTimer, function() + ESX.ShowNotification(TranslateCap('unrestrained_timer')) + TriggerEvent('esx_policejob:unrestrain') + handcuffTimer.active = false + end) end --- TODO --- - return to garage if owned --- - message owner that his vehicle has been impounded function ImpoundVehicle(vehicle) - --local vehicleName = GetLabelText(GetDisplayNameFromVehicleModel(GetEntityModel(vehicle))) - ESX.Game.DeleteVehicle(vehicle) - ESX.ShowNotification(TranslateCap('impound_successful')) - currentTask.busy = false + ESX.Game.DeleteVehicle(vehicle) + ESX.ShowNotification(TranslateCap('impound_successful')) + currentTask.busy = false end diff --git a/[esx_addons]/esx_policejob/client/vehicle.lua b/[esx_addons]/esx_policejob/client/vehicle.lua index 9ac134a3..69a9ae09 100644 --- a/[esx_addons]/esx_policejob/client/vehicle.lua +++ b/[esx_addons]/esx_policejob/client/vehicle.lua @@ -1,320 +1,360 @@ +local spawnedVehicles = spawnedVehicles or {} +isInShopMenu = isInShopMenu or false -local spawnedVehicles = {} - -function OpenVehicleSpawnerMenu(type, station, part, partNum) - local playerCoords = GetEntityCoords(PlayerPedId()) - local elements = { - {unselectable = true, icon = "fas fa-car", title = TranslateCap('garage_title')}, - {icon = "fas fa-car", title = TranslateCap('garage_storeditem'), action = 'garage'}, - {icon = "fas fa-car", title = TranslateCap('garage_storeitem'), action = 'store_garage'}, - {icon = "fas fa-car", title = TranslateCap('garage_buyitem'), action = 'buy_vehicle'} - } - - ESX.OpenContext("right", elements, function(menu,element) - if element.action == "buy_vehicle" then - local shopElements = {} - local shopCoords = Config.PoliceStations[station][part][partNum].InsideShop - local authorizedVehicles = Config.AuthorizedVehicles[type][ESX.PlayerData.job.grade_name] - - if authorizedVehicles then - if #authorizedVehicles > 0 then - for k,vehicle in ipairs(authorizedVehicles) do - if IsModelInCdimage(vehicle.model) then - local vehicleLabel = GetLabelText(GetDisplayNameFromVehicleModel(vehicle.model)) - - shopElements[#shopElements+1] = { - icon = 'fas fa-car', - title = ('%s - %s'):format(vehicleLabel, TranslateCap('shop_item', ESX.Math.GroupDigits(vehicle.price))), - name = vehicleLabel, - model = vehicle.model, - price = vehicle.price, - props = vehicle.props, - type = type - } - end - end - - if #shopElements > 0 then - OpenShopMenu(shopElements, playerCoords, shopCoords) - else - ESX.ShowNotification(TranslateCap('garage_notauthorized')) - end - else - ESX.ShowNotification(TranslateCap('garage_notauthorized')) - end - else - ESX.ShowNotification(TranslateCap('garage_notauthorized')) - end - elseif element.action == "garage" then - local garage = { - {unselectable = true, icon = "fas fa-car", title = "Garage"} - } - - ESX.TriggerServerCallback('esx_vehicleshop:retrieveJobVehicles', function(jobVehicles) - if #jobVehicles > 0 then - local allVehicleProps = {} - - for k,v in ipairs(jobVehicles) do - local props = json.decode(v.vehicle) - - if IsModelInCdimage(props.model) then - local vehicleName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) - local label = ('%s - %s: '):format(vehicleName, props.plate) - - if v.stored == 1 or v.stored == true then - label = label .. ('%s'):format(TranslateCap('garage_stored')) - elseif v.stored == 0 or v.stored == false then - label = label .. ('%s'):format(TranslateCap('garage_notstored')) - end - - garage[#garage+1] = { - icon = 'fas fa-car', - title = label, - stored = v.stored, - model = props.model, - plate = props.plate - } - - allVehicleProps[props.plate] = props - end - end - - if #garage > 0 then - ESX.OpenContext("right", garage, function(menuG,elementG) - if elementG.stored == 1 or elementG.stored == true then - local foundSpawn, spawnPoint = GetAvailableVehicleSpawnPoint(station, part, partNum) - - if foundSpawn then - ESX.CloseContext() - - ESX.Game.SpawnVehicle(elementG.model, spawnPoint.coords, spawnPoint.heading, function(vehicle) - local vehicleProps = allVehicleProps[elementG.plate] - ESX.Game.SetVehicleProperties(vehicle, vehicleProps) - - TriggerServerEvent('esx_vehicleshop:setJobVehicleState', elementG.plate, false) - ESX.ShowNotification(TranslateCap('garage_released')) - end) - end - else - ESX.ShowNotification(TranslateCap('garage_notavailable')) - end - end) - else - ESX.ShowNotification(TranslateCap('garage_empty')) - end - else - ESX.ShowNotification(TranslateCap('garage_empty')) - end - end, type) - elseif element.action == "store_garage" then - StoreNearbyVehicle(playerCoords) - end - end) +local function safeGetJobGrade() + return (ESX and ESX.PlayerData and ESX.PlayerData.job and ESX.PlayerData.job.grade_name) or 'recruit' end -function StoreNearbyVehicle(playerCoords) - local vehicles, plates, index = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}, {} - - if next(vehicles) then - for i = 1, #vehicles do - local vehicle = vehicles[i] - - -- Make sure the vehicle we're saving is empty, or else it won't be deleted - if GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1) then - local plate = ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)) - plates[#plates + 1] = plate - index[plate] = vehicle - end - end - else - ESX.ShowNotification(TranslateCap('garage_store_nearby')) - return - end - - ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(plate) - if plate then - local vehicleId = index[plate] - local attempts = 0 - ESX.Game.DeleteVehicle(vehicleId) - local isBusy = true - - CreateThread(function() - BeginTextCommandBusyspinnerOn('STRING') - AddTextComponentSubstringPlayerName(TranslateCap('garage_storing')) - EndTextCommandBusyspinnerOn(4) - - while isBusy do - Wait(100) - end - - BusyspinnerOff() - end) - - -- Workaround for vehicle not deleting when other players are near it. - while DoesEntityExist(vehicleId) do - Wait(500) - attempts = attempts + 1 - - -- Give up - if attempts > 30 then - break - end - - vehicles = ESX.Game.GetVehiclesInArea(playerCoords, 30.0) - if #vehicles > 0 then - for i = 1, #vehicles do - local vehicle = vehicles[i] - if ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)) == plate then - ESX.Game.DeleteVehicle(vehicle) - break - end - end - end - end - - isBusy = false - ESX.ShowNotification(TranslateCap('garage_has_stored')) - else - ESX.ShowNotification(TranslateCap('garage_has_notstored')) - end - end, plates) +local function modelToLabel(model) + local display = GetDisplayNameFromVehicleModel(model) + local label = (display and display ~= '' and GetLabelText(display)) or tostring(model) + if not label or label == 'NULL' then label = tostring(model) end + return label end -function GetAvailableVehicleSpawnPoint(station, part, partNum) - local spawnPoints = Config.PoliceStations[station][part][partNum].SpawnPoints - local found, foundSpawnPoint = false, nil - - for i=1, #spawnPoints, 1 do - if ESX.Game.IsSpawnPointClear(spawnPoints[i].coords, spawnPoints[i].radius) then - found, foundSpawnPoint = true, spawnPoints[i] - break - end - end - - if found then - return true, foundSpawnPoint - else - ESX.ShowNotification(TranslateCap('vehicle_blocked')) - return false - end +local function ensureModelLoaded(modelHash, spinnerText) + modelHash = (type(modelHash) == 'number' and modelHash or joaat(modelHash)) + if HasModelLoaded(modelHash) then return true end + + RequestModel(modelHash) + + BeginTextCommandBusyspinnerOn('STRING') + AddTextComponentSubstringPlayerName(spinnerText or TranslateCap('vehicleshop_awaiting_model')) + EndTextCommandBusyspinnerOn(4) + + local tries = 0 + while not HasModelLoaded(modelHash) do + Wait(0) + DisableAllControlActions(0) + tries = tries + 1 + if tries > 1000 then + break + end + end + + BusyspinnerOff() + return HasModelLoaded(modelHash) end -function OpenShopMenu(elements, restoreCoords, shopCoords) - local playerPed = PlayerPedId() - isInShopMenu = true - ESX.OpenContext("right", elements, function(menu,element) - local elements2 = { - {unselectable = true, icon = "fas fa-car", title = element.title}, - {icon = "fas fa-eye", title = TranslateCap('view'), value = "view"} - } - - ESX.OpenContext("right", elements2, function(menu2,element2) - if element2.value == "view" then - DeleteSpawnedVehicles() - WaitForVehicleToLoad(element.model) - - ESX.Game.SpawnLocalVehicle(element.model, shopCoords, 0.0, function(vehicle) - table.insert(spawnedVehicles, vehicle) - TaskWarpPedIntoVehicle(playerPed, vehicle, -1) - FreezeEntityPosition(vehicle, true) - SetModelAsNoLongerNeeded(element.model) - - if element.props then - ESX.Game.SetVehicleProperties(vehicle, element.props) - end - end) - - local elements3 = { - {unselectable = true, icon = "fas fa-car", title = element.title}, - {icon = "fas fa-check-double", title = TranslateCap('buy_car'), value = "buy"}, - {icon = "fas fa-eye", title = TranslateCap('stop_view'), value = "stop"} - } - - ESX.OpenContext("right", elements3, function(menu3,element3) - if element3.value == 'stop' then - isInShopMenu = false - ESX.CloseContext() - - DeleteSpawnedVehicles() - FreezeEntityPosition(playerPed, false) - SetEntityVisible(playerPed, true) - - ESX.Game.Teleport(playerPed, restoreCoords) - elseif element3.value == "buy" then - local newPlate = exports['esx_vehicleshop']:GeneratePlate() - local vehicle = GetVehiclePedIsIn(playerPed, false) - local props = ESX.Game.GetVehicleProperties(vehicle) - props.plate = newPlate - - ESX.TriggerServerCallback('esx_policejob:buyJobVehicle', function (bought) - if bought then - ESX.ShowNotification(TranslateCap('vehicleshop_bought', element.name, ESX.Math.GroupDigits(element.price))) - - isInShopMenu = false - ESX.CloseContext() - DeleteSpawnedVehicles() - FreezeEntityPosition(playerPed, false) - SetEntityVisible(playerPed, true) - - ESX.Game.Teleport(playerPed, restoreCoords) - else - ESX.ShowNotification(TranslateCap('vehicleshop_money')) - ESX.CloseContext() - end - end, props, element.type) - end - end, function() - isInShopMenu = false - ESX.CloseContext() - - DeleteSpawnedVehicles() - FreezeEntityPosition(playerPed, false) - SetEntityVisible(playerPed, true) - - ESX.Game.Teleport(playerPed, restoreCoords) - end) - end - end) - end) +local function safeInsideShopCoords(station, part, partNum) + local cfg = Config.PoliceStations[station][part][partNum] + local shop = cfg.InsideShop + if shop and shop.x then + local heading = shop.w or 0.0 + return vector3(shop.x, shop.y, shop.z), heading + end + if type(shop) == 'vector4' then + return vector3(shop.x, shop.y, shop.z), shop.w + elseif type(shop) == 'vector3' then + return shop, 0.0 + end + local sp = cfg.SpawnPoints and cfg.SpawnPoints[1] + if sp then return sp.coords, (sp.heading or 0.0) end + return GetEntityCoords(PlayerPedId()), 0.0 end +local function getAuthorizedVehiclesFor(typeKey, grade) + local byType = Config.AuthorizedVehicles and Config.AuthorizedVehicles[typeKey] + if not byType then return {} end + return byType[grade] or {} +end -CreateThread(function() - while true do - Wait(0) - - if isInShopMenu then - DisableControlAction(0, 75, true) -- Disable exit vehicle - DisableControlAction(27, 75, true) -- Disable exit vehicle - else - Wait(500) - end - end -end) +local function tryGeneratePlateFallback() + local plate = ('%s%03d%s'):format(string.char(math.random(65,90), math.random(65,90)), + math.random(0,999), + string.char(math.random(65,90), math.random(65,90))) + return ESX and ESX.Math and ESX.Math.Trim and ESX.Math.Trim(plate:upper()) or plate:upper() +end -function DeleteSpawnedVehicles() - while #spawnedVehicles > 0 do - local vehicle = spawnedVehicles[1] - ESX.Game.DeleteVehicle(vehicle) - table.remove(spawnedVehicles, 1) - end +local function deleteSpawnedPreview() + while #spawnedVehicles > 0 do + local veh = spawnedVehicles[1] + ESX.Game.DeleteVehicle(veh) + table.remove(spawnedVehicles, 1) + end end -function WaitForVehicleToLoad(modelHash) - modelHash = (type(modelHash) == 'number' and modelHash or joaat(modelHash)) +local function getAvailableSpawnPoint(station, part, partNum) + local spawnPoints = (Config.PoliceStations[station] and Config.PoliceStations[station][part] + and Config.PoliceStations[station][part][partNum] + and Config.PoliceStations[station][part][partNum].SpawnPoints) or {} - if not HasModelLoaded(modelHash) then - RequestModel(modelHash) + for i=1, #spawnPoints do + local sp = spawnPoints[i] + if ESX.Game.IsSpawnPointClear(sp.coords, sp.radius) then + return true, sp + end + end - BeginTextCommandBusyspinnerOn('STRING') - AddTextComponentSubstringPlayerName(TranslateCap('vehicleshop_awaiting_model')) - EndTextCommandBusyspinnerOn(4) + ESX.ShowNotification(TranslateCap('vehicle_blocked')) + return false +end + +function OpenVehicleSpawnerMenu(typeKey, station, part, partNum) + local ped = PlayerPedId() + local playerCoords = GetEntityCoords(ped) + + local menuEls = { + {unselectable = true, icon = "fas fa-car", title = TranslateCap('garage_title')}, + {icon = "fas fa-car", title = TranslateCap('garage_storeditem'), action = 'garage'}, + {icon = "fas fa-car", title = TranslateCap('garage_storeitem'), action = 'store_garage'}, + {icon = "fas fa-car", title = TranslateCap('garage_buyitem'), action = 'buy_vehicle'} + } + + ESX.OpenContext("right", menuEls, function(_, element) + if element.action == "buy_vehicle" then + local grade = safeGetJobGrade() + local auth = getAuthorizedVehiclesFor(typeKey, grade) + local shopElements = {} + + if auth and #auth > 0 then + for _, vehicle in ipairs(auth) do + if vehicle.model and IsModelInCdimage(joaat(vehicle.model)) then + local label = modelToLabel(joaat(vehicle.model)) + shopElements[#shopElements+1] = { + icon = 'fas fa-car', + title = ('%s - %s') + :format(label, TranslateCap('shop_item', ESX.Math.GroupDigits(vehicle.price))), + name = label, + model = vehicle.model, + price = vehicle.price or 0, + props = vehicle.props, + type = typeKey + } + end + end + end + + if #shopElements > 0 then + local shopPos, _ = safeInsideShopCoords(station, part, partNum) + OpenShopMenu(shopElements, playerCoords, shopPos, station, part, partNum) + else + ESX.ShowNotification(TranslateCap('garage_notauthorized')) + end + + elseif element.action == "garage" then + local list = { + {unselectable = true, icon = "fas fa-car", title = "Garage"} + } + + ESX.TriggerServerCallback('esx_vehicleshop:retrieveJobVehicles', function(jobVehicles) + if jobVehicles and #jobVehicles > 0 then + local allPropsByPlate = {} + + for _, v in ipairs(jobVehicles) do + local props = v.vehicle and json.decode(v.vehicle) + if props and props.model and IsModelInCdimage(props.model) then + local label = modelToLabel(props.model) + local part1 = ('%s - %s: '):format(label, props.plate) + local stored = (v.stored == 1 or v.stored == true) + local part2 = stored and + ('%s'):format(TranslateCap('garage_stored')) or + ('%s'):format(TranslateCap('garage_notstored')) + + list[#list+1] = { + icon = 'fas fa-car', + title = part1 .. part2, + stored = stored, + model = props.model, + plate = props.plate + } + allPropsByPlate[props.plate] = props + end + end + + if #list > 1 then + ESX.OpenContext("right", list, function(_, elementG) + if elementG.stored then + local ok, sp = getAvailableSpawnPoint(station, part, partNum) + if ok then + ESX.CloseContext() + ESX.Game.SpawnVehicle(elementG.model, sp.coords, sp.heading or 0.0, function(vehicle) + local props = allPropsByPlate[elementG.plate] + if props then ESX.Game.SetVehicleProperties(vehicle, props) end + SetVehicleOnGroundProperly(vehicle) + SetVehicleEngineOn(vehicle, true, true, false) + TriggerServerEvent('esx_vehicleshop:setJobVehicleState', elementG.plate, false) + ESX.ShowNotification(TranslateCap('garage_released')) + end) + end + else + ESX.ShowNotification(TranslateCap('garage_notavailable')) + end + end) + else + ESX.ShowNotification(TranslateCap('garage_empty')) + end + else + ESX.ShowNotification(TranslateCap('garage_empty')) + end + end, typeKey) + + elseif element.action == "store_garage" then + StoreNearbyVehicle(playerCoords) + end + end) +end - while not HasModelLoaded(modelHash) do - Wait(0) - DisableAllControlActions(0) - end +function StoreNearbyVehicle(playerCoords) + local vehicles, plates, index = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}, {} + + if not vehicles or #vehicles == 0 then + ESX.ShowNotification(TranslateCap('garage_store_nearby')) + return + end + + for i=1, #vehicles do + local veh = vehicles[i] + if GetVehicleNumberOfPassengers(veh) == 0 and IsVehicleSeatFree(veh, -1) then + local plate = ESX.Math.Trim(GetVehicleNumberPlateText(veh)) + plates[#plates+1] = plate + index[plate] = veh + end + end + + ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(plate) + if plate then + local vehicleId = index[plate] + local attempts = 0 + ESX.Game.DeleteVehicle(vehicleId) + local isBusy = true + + CreateThread(function() + BeginTextCommandBusyspinnerOn('STRING') + AddTextComponentSubstringPlayerName(TranslateCap('garage_storing')) + EndTextCommandBusyspinnerOn(4) + while isBusy do Wait(100) end + BusyspinnerOff() + end) + + while DoesEntityExist(vehicleId) do + Wait(500) + attempts = attempts + 1 + if attempts > 30 then break end + + local around = ESX.Game.GetVehiclesInArea(playerCoords, 30.0) + for i=1, #around do + local veh = around[i] + if ESX.Math.Trim(GetVehicleNumberPlateText(veh)) == plate then + ESX.Game.DeleteVehicle(veh) + break + end + end + end + + isBusy = false + ESX.ShowNotification(TranslateCap('garage_has_stored')) + else + ESX.ShowNotification(TranslateCap('garage_has_notstored')) + end + end, plates) +end - BusyspinnerOff() - end +function OpenShopMenu(elements, restoreCoords, shopCoords, station, part, partNum) + local playerPed = PlayerPedId() + isInShopMenu = true + + ESX.OpenContext("right", elements, function(_, element) + local actions = { + {unselectable = true, icon = "fas fa-car", title = element.title}, + {icon = "fas fa-eye", title = TranslateCap('view'), value = "view"} + } + + ESX.OpenContext("right", actions, function(_, element2) + if element2.value ~= "view" then return end + + deleteSpawnedPreview() + SetEntityCoordsNoOffset(playerPed, shopCoords.x, shopCoords.y, shopCoords.z, false, false, false) + SetEntityHeading(playerPed, 0.0) + FreezeEntityPosition(playerPed, true) + SetEntityVisible(playerPed, false, false) + + if not ensureModelLoaded(element.model, TranslateCap('vehicleshop_awaiting_model')) then + FreezeEntityPosition(playerPed, false) + SetEntityVisible(playerPed, true, false) + ESX.ShowNotification(TranslateCap('garage_notauthorized')) + return + end + + ESX.Game.SpawnLocalVehicle(element.model, shopCoords, 0.0, function(vehicle) + table.insert(spawnedVehicles, vehicle) + SetModelAsNoLongerNeeded(joaat(element.model)) + if element.props then + ESX.Game.SetVehicleProperties(vehicle, element.props) + end + SetVehicleOnGroundProperly(vehicle) + FreezeEntityPosition(vehicle, true) + TaskWarpPedIntoVehicle(playerPed, vehicle, -1) + end) + + local confirm = { + {unselectable = true, icon = "fas fa-car", title = element.title}, + {icon = "fas fa-check-double", title = TranslateCap('buy_car'), value = "buy"}, + {icon = "fas fa-eye", title = TranslateCap('stop_view'), value = "stop"} + } + + ESX.OpenContext("right", confirm, function(_, choose) + if choose.value == 'stop' then + isInShopMenu = false + ESX.CloseContext() + deleteSpawnedPreview() + FreezeEntityPosition(playerPed, false) + SetEntityVisible(playerPed, true, false) + ESX.Game.Teleport(playerPed, restoreCoords) + + elseif choose.value == "buy" then + local newPlate + if exports and exports['esx_vehicleshop'] and exports['esx_vehicleshop'].GeneratePlate then + newPlate = exports['esx_vehicleshop']:GeneratePlate() + else + newPlate = tryGeneratePlateFallback() + end + + local vehicle = GetVehiclePedIsIn(playerPed, false) + local props = ESX.Game.GetVehicleProperties(vehicle) + props.plate = newPlate + + ESX.TriggerServerCallback('esx_policejob:buyJobVehicle', function (bought) + if bought then + ESX.ShowNotification(TranslateCap('vehicleshop_bought', element.name, ESX.Math.GroupDigits(element.price))) + isInShopMenu = false + ESX.CloseContext() + deleteSpawnedPreview() + FreezeEntityPosition(playerPed, false) + SetEntityVisible(playerPed, true, false) + ESX.Game.Teleport(playerPed, restoreCoords) + else + ESX.ShowNotification(TranslateCap('vehicleshop_money')) + ESX.CloseContext() + end + end, props, element.type) + end + end, function() + isInShopMenu = false + ESX.CloseContext() + deleteSpawnedPreview() + FreezeEntityPosition(playerPed, false) + SetEntityVisible(playerPed, true, false) + ESX.Game.Teleport(playerPed, restoreCoords) + end) + end) + end) end + +CreateThread(function() + while true do + if isInShopMenu then + DisableControlAction(0, 75, true) -- Exit vehicle + DisableControlAction(27, 75, true) + DisableControlAction(0, 63, true) -- Turn left + DisableControlAction(0, 64, true) -- Turn right + DisableControlAction(0, 71, true) -- Forward + DisableControlAction(0, 72, true) -- Backward + DisableControlAction(0, 69, true) -- Attack + DisableControlAction(0, 70, true) + Wait(0) + else + Wait(400) + end + end +end) diff --git a/[esx_addons]/esx_policejob/fxmanifest.lua b/[esx_addons]/esx_policejob/fxmanifest.lua index a00c413f..851f635d 100644 --- a/[esx_addons]/esx_policejob/fxmanifest.lua +++ b/[esx_addons]/esx_policejob/fxmanifest.lua @@ -1,5 +1,4 @@ fx_version 'adamant' - game 'gta5' description 'Allows Players to RP as Police Officers (cars, outfits, handcuffing etc)' @@ -13,14 +12,16 @@ server_scripts { '@oxmysql/lib/MySQL.lua', '@es_extended/locale.lua', 'locales/*.lua', - 'config.lua', + 'shared/config.lua', + 'shared/constants.lua', + 'shared/permissions.lua', 'server/*.lua' } client_scripts { '@es_extended/locale.lua', 'locales/*.lua', - 'config.lua', + 'shared/config.lua', 'client/*.lua' } @@ -28,4 +29,4 @@ dependencies { 'es_extended', 'esx_billing', 'esx_vehicleshop' -} +} \ No newline at end of file diff --git a/[esx_addons]/esx_policejob/server/main.lua b/[esx_addons]/esx_policejob/server/main.lua index d1fa3df8..8ed6bd2b 100644 --- a/[esx_addons]/esx_policejob/server/main.lua +++ b/[esx_addons]/esx_policejob/server/main.lua @@ -1,437 +1,479 @@ -if Config.EnableESXService then - if Config.MaxInService ~= -1 then - TriggerEvent('esx_service:activateService', 'police', Config.MaxInService) - end +local ESX = exports['es_extended']:getSharedObject() + +local function getXPlayer(src) + return ESX.GetPlayerFromId and ESX.GetPlayerFromId(src) or (ESX.Player and ESX.Player(src)) or nil end -TriggerEvent('esx_phone:registerNumber', 'police', TranslateCap('alert_police'), true, true) -TriggerEvent('esx_society:registerSociety', 'police', TranslateCap('society_police'), 'society_police', 'society_police', 'society_police', {type = 'public'}) - -RegisterNetEvent('esx_policejob:confiscatePlayerItem') -AddEventHandler('esx_policejob:confiscatePlayerItem', function(target, itemType, itemName, amount) - local source = source - local sourceXPlayer = ESX.Player(source) - local targetXPlayer = ESX.Player(target) - local job = sourceXPlayer.getJob() - local sourceXPlayerName = sourceXPlayer.getName() - local targetXPlayerName = targetXPlayer.getName() - if job.name ~= 'police' then - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Exploit The Confuscation System!'):format(sourceXPlayer.src)) - return - end - - if itemType == 'item_standard' then - local targetItem = targetXPlayer.getInventoryItem(itemName) - local sourceItem = sourceXPlayer.getInventoryItem(itemName) - - -- does the target player have enough in their inventory? - if targetItem and targetItem.count >= amount then - - -- can the player carry the said amount of x item? - if sourceXPlayer.canCarryItem(itemName, amount) then - targetXPlayer.removeInventoryItem(itemName, amount) - sourceXPlayer.addInventoryItem (itemName, amount) - sourceXPlayer.showNotification(TranslateCap('you_confiscated', amount, sourceItem.label, targetXPlayerName)) - targetXPlayer.showNotification(TranslateCap('got_confiscated', amount, sourceItem.label, sourceXPlayerName)) - else - sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) - end - else - sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) - end - - elseif itemType == 'item_account' then - local targetAccount = targetXPlayer.getAccount(itemName) - - -- does the target player have enough money? - if targetAccount.money >= amount then - targetXPlayer.removeAccountMoney(itemName, amount, "Confiscated") - sourceXPlayer.addAccountMoney (itemName, amount, "Confiscated") - - sourceXPlayer.showNotification(TranslateCap('you_confiscated_account', amount, itemName, targetXPlayerName)) - targetXPlayer.showNotification(TranslateCap('got_confiscated_account', amount, itemName, sourceXPlayerName)) - else - sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) - end - - elseif itemType == 'item_weapon' then - if amount == nil then amount = 0 end - - -- does the target player have weapon? - if targetXPlayer.hasWeapon(itemName) then - targetXPlayer.removeWeapon(itemName) - sourceXPlayer.addWeapon (itemName, amount) - - sourceXPlayer.showNotification(TranslateCap('you_confiscated_weapon', ESX.GetWeaponLabel(itemName), targetXPlayerName, amount)) - targetXPlayer.showNotification(TranslateCap('got_confiscated_weapon', ESX.GetWeaponLabel(itemName), amount, sourceXPlayerName)) - else - sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) - end - end -end) +local function getJob(xPlayer) return xPlayer and (xPlayer.job or xPlayer.getJob and xPlayer.getJob()) end +local function getIdentifier(xPlayer) return xPlayer and (xPlayer.identifier or xPlayer.getIdentifier and xPlayer.getIdentifier()) end +local function playerHasWeapon(xPlayer, name) return xPlayer.hasWeapon and xPlayer.hasWeapon(name) or false end -RegisterNetEvent('esx_policejob:handcuff') -AddEventHandler('esx_policejob:handcuff', function(target) - local xPlayer = ESX.Player(source) +local function safeNumber(n, fallback) + n = tonumber(n) + if not n or n ~= n or n == math.huge or n == -math.huge then return tonumber(fallback) or 0 end + return n +end - if xPlayer.getJob().name == 'police' then - TriggerClientEvent('esx_policejob:handcuff', target) - else - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Exploit Handcuffs!'):format(xPlayer.src)) - end +local function getPaymentAccount() + if Config and Config.ArmoryAccount == 'bank' then return 'bank' end + return 'money' +end + +CreateThread(function() + if Config and Config.EnableESXService and GetResourceState('esx_service') == 'started' then + if safeNumber(Config.MaxInService, -1) ~= -1 then + TriggerEvent('esx_service:activateService', 'police', safeNumber(Config.MaxInService, 10)) + end + end + + if GetResourceState('esx_phone') == 'started' then + TriggerEvent('esx_phone:registerNumber', 'police', TranslateCap('alert_police'), true, true) + end + + if GetResourceState('esx_society') == 'started' then + TriggerEvent('esx_society:registerSociety', 'police', TranslateCap('society_police'), + 'society_police', 'society_police', 'society_police', { type = 'public' }) + end end) -RegisterNetEvent('esx_policejob:drag') -AddEventHandler('esx_policejob:drag', function(target) - local xPlayer = ESX.Player(source) +local CONST_OK, CONST = pcall(require, 'shared/constants') +local PERMS_OK, PERMS = pcall(require, 'shared/permissions') + +local JOB_NAME = (CONST_OK and CONST and CONST.JOB_NAME) or 'police' + +local function isPolice(xPlayer) + local job = getJob(xPlayer) + return job and (job.name == JOB_NAME) +end + +local function hasPerm(xPlayer, perm) + if not isPolice(xPlayer) then return false end + if not (PERMS_OK and PERMS and PERMS.jobs and PERMS.jobs[JOB_NAME]) then + return true + end + local job = getJob(xPlayer) + local g = job and job.grade or job and job.grade_name + local info + if type(g) == 'number' then + info = PERMS.jobs[JOB_NAME].grades and PERMS.jobs[JOB_NAME].grades[g] + else + info = PERMS.jobs[JOB_NAME].grades_by_name and PERMS.jobs[JOB_NAME].grades_by_name[g] + end + return info and info[perm] == true +end + +local function warnExploit(xPlayer, what) + print(('[^3WARNING^7] Player ^5%s^7 attempted: %s'):format(xPlayer and xPlayer.source or 'unknown', tostring(what))) +end + +RegisterNetEvent('esx_policejob:confiscatePlayerItem', function(target, itemType, itemName, amount) + local src = source + local sourceXPlayer = getXPlayer(src) + local targetXPlayer = getXPlayer(target) + if not sourceXPlayer or not targetXPlayer then return end + if not hasPerm(sourceXPlayer, 'canSearch') then return warnExploit(sourceXPlayer, 'Confiscation') end + + amount = safeNumber(amount, 0) + if itemType == 'item_standard' then + local tItem = targetXPlayer.getInventoryItem(itemName) + if amount > 0 and tItem and tItem.count >= amount then + if sourceXPlayer.canCarryItem(itemName, amount) then + targetXPlayer.removeInventoryItem(itemName, amount) + sourceXPlayer.addInventoryItem(itemName, amount) + sourceXPlayer.showNotification(TranslateCap('you_confiscated', amount, (sourceXPlayer.getInventoryItem(itemName).label or itemName), targetXPlayer.getName())) + targetXPlayer.showNotification(TranslateCap('got_confiscated', amount, (tItem.label or itemName), sourceXPlayer.getName())) + else + sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) + end + else + sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) + end + + elseif itemType == 'item_account' then + local acc = targetXPlayer.getAccount(itemName) + if amount > 0 and acc and acc.money >= amount then + targetXPlayer.removeAccountMoney(itemName, amount, "Confiscated") + sourceXPlayer.addAccountMoney(itemName, amount, "Confiscated") + sourceXPlayer.showNotification(TranslateCap('you_confiscated_account', amount, itemName, targetXPlayer.getName())) + targetXPlayer.showNotification(TranslateCap('got_confiscated_account', amount, itemName, sourceXPlayer.getName())) + else + sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) + end + + elseif itemType == 'item_weapon' then + local ammo = amount or 0 + if playerHasWeapon(targetXPlayer, itemName) then + targetXPlayer.removeWeapon(itemName) + sourceXPlayer.addWeapon(itemName, ammo) + sourceXPlayer.showNotification(TranslateCap('you_confiscated_weapon', ESX.GetWeaponLabel(itemName), targetXPlayer.getName(), ammo)) + targetXPlayer.showNotification(TranslateCap('got_confiscated_weapon', ESX.GetWeaponLabel(itemName), ammo, sourceXPlayer.getName())) + else + sourceXPlayer.showNotification(TranslateCap('quantity_invalid')) + end + end +end) - if xPlayer.getJob().name == 'police' then - TriggerClientEvent('esx_policejob:drag', target, source) - else - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Exploit Dragging!'):format(xPlayer.src)) - end +RegisterNetEvent('esx_policejob:handcuff', function(target) + local xPlayer = getXPlayer(source); if not xPlayer then return end + if hasPerm(xPlayer, 'canCuff') then + TriggerClientEvent('esx_policejob:handcuff', target) + else + warnExploit(xPlayer, 'Handcuffs') + end end) -RegisterNetEvent('esx_policejob:putInVehicle') -AddEventHandler('esx_policejob:putInVehicle', function(target) - local xPlayer = ESX.Player(source) +RegisterNetEvent('esx_policejob:cuffsState', function(isCuffed) + local src = source + TriggerClientEvent('esx_policejob:cuffsSync', -1, src, isCuffed and true or false) +end) - if xPlayer.getJob().name == 'police' then - TriggerClientEvent('esx_policejob:putInVehicle', target) - else - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Exploit Garage!'):format(xPlayer.src)) - end +RegisterNetEvent('esx_policejob:drag', function(target) + local xPlayer = getXPlayer(source) + if xPlayer and isPolice(xPlayer) then + TriggerClientEvent('esx_policejob:drag', target, source) + else + warnExploit(xPlayer, 'Dragging') + end end) -RegisterNetEvent('esx_policejob:OutVehicle') -AddEventHandler('esx_policejob:OutVehicle', function(target) - local xPlayer = ESX.Player(source) +RegisterNetEvent('esx_policejob:putInVehicle', function(target) + local xPlayer = getXPlayer(source) + if xPlayer and isPolice(xPlayer) then + TriggerClientEvent('esx_policejob:putInVehicle', target) + else + warnExploit(xPlayer, 'PutInVehicle') + end +end) - if xPlayer.getJob().name == 'police' then - TriggerClientEvent('esx_policejob:OutVehicle', target) - else - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Exploit Dragging Out Of Vehicle!'):format(xPlayer.src)) - end +RegisterNetEvent('esx_policejob:OutVehicle', function(target) + local xPlayer = getXPlayer(source) + if xPlayer and isPolice(xPlayer) then + TriggerClientEvent('esx_policejob:OutVehicle', target) + else + warnExploit(xPlayer, 'OutVehicle') + end end) -RegisterNetEvent('esx_policejob:getStockItem') -AddEventHandler('esx_policejob:getStockItem', function(itemName, count) - local source = source - local xPlayer = ESX.Player(source) - - TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory) - local inventoryItem = inventory.getItem(itemName) - - -- is there enough in the society? - if count > 0 and inventoryItem.count >= count then - - -- can the player carry the said amount of x item? - if xPlayer.canCarryItem(itemName, count) then - inventory.removeItem(itemName, count) - xPlayer.addInventoryItem(itemName, count) - xPlayer.showNotification(TranslateCap('have_withdrawn', count, inventoryItem.name)) - else - xPlayer.showNotification(TranslateCap('quantity_invalid')) - end - else - xPlayer.showNotification(TranslateCap('quantity_invalid')) - end - end) +RegisterNetEvent('esx_policejob:getStockItem', function(itemName, count) + local xPlayer = getXPlayer(source) + if not xPlayer or not isPolice(xPlayer) then return end + if GetResourceState('esx_addoninventory') ~= 'started' then return end + + TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory) + local inventoryItem = inventory.getItem(itemName) + count = safeNumber(count, 0) + if count > 0 and inventoryItem and inventoryItem.count >= count then + if xPlayer.canCarryItem(itemName, count) then + inventory.removeItem(itemName, count) + xPlayer.addInventoryItem(itemName, count) + xPlayer.showNotification(TranslateCap('have_withdrawn', count, inventoryItem.name)) + else + xPlayer.showNotification(TranslateCap('quantity_invalid')) + end + else + xPlayer.showNotification(TranslateCap('quantity_invalid')) + end + end) end) -RegisterNetEvent('esx_policejob:putStockItems') -AddEventHandler('esx_policejob:putStockItems', function(itemName, count) - local xPlayer = ESX.Player(source) - local sourceItem = xPlayer.getInventoryItem(itemName) - - TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory) - local inventoryItem = inventory.getItem(itemName) - - -- does the player have enough of the item? - if sourceItem.count >= count and count > 0 then - xPlayer.removeInventoryItem(itemName, count) - inventory.addItem(itemName, count) - xPlayer.showNotification(TranslateCap('have_deposited', count, inventoryItem.name)) - else - xPlayer.showNotification(TranslateCap('quantity_invalid')) - end - end) +RegisterNetEvent('esx_policejob:putStockItems', function(itemName, count) + local xPlayer = getXPlayer(source) + if not xPlayer or not isPolice(xPlayer) then return end + if GetResourceState('esx_addoninventory') ~= 'started' then return end + + local srcItem = xPlayer.getInventoryItem(itemName) + TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory) + count = safeNumber(count, 0) + if srcItem and srcItem.count >= count and count > 0 then + xPlayer.removeInventoryItem(itemName, count) + inventory.addItem(itemName, count) + xPlayer.showNotification(TranslateCap('have_deposited', count, srcItem.label or itemName)) + else + xPlayer.showNotification(TranslateCap('quantity_invalid')) + end + end) end) ESX.RegisterServerCallback('esx_policejob:getOtherPlayerData', function(source, cb, target, notify) - local xPlayer = ESX.Player(target) - local job = xPlayer.getJob() - if notify then - xPlayer.showNotification(TranslateCap('being_searched')) - end - - if xPlayer then - local data = { - name = xPlayer.getName(), - job = job.label, - grade = job.grade_label, - inventory = xPlayer.getInventory(), - accounts = xPlayer.getAccounts(), - weapons = xPlayer.getLoadout() - } - - if Config.EnableESXIdentity then - data.dob = xPlayer.get('dateofbirth') - data.height = xPlayer.get('height') - - if xPlayer.get('sex') == 'm' then data.sex = 'male' else data.sex = 'female' end - end - - TriggerEvent('esx_status:getStatus', target, 'drunk', function(status) - if status then - data.drunk = ESX.Math.Round(status.percent) - end - end) - - if Config.EnableLicenses then - TriggerEvent('esx_license:getLicenses', target, function(licenses) - data.licenses = licenses - cb(data) - end) - else - cb(data) - end - end + local xTarget = getXPlayer(target) + if not xTarget then return cb(nil) end + + if notify and GetResourceState('esx_status') == 'started' then + xTarget.showNotification(TranslateCap('being_searched')) + end + + local job = getJob(xTarget) + local data = { + name = xTarget.getName and xTarget.getName() or GetPlayerName(target), + job = job and (job.label or job.name) or 'Unknown', + grade = job and (job.grade_label or job.grade_name or job.grade) or '', + inventory = xTarget.getInventory and xTarget.getInventory() or {}, + accounts = xTarget.getAccounts and xTarget.getAccounts() or {}, + weapons = xTarget.getLoadout and xTarget.getLoadout() or {} + } + + local function finish() + if Config.EnableLicenses and GetResourceState('esx_license') == 'started' then + TriggerEvent('esx_license:getLicenses', target, function(licenses) + data.licenses = licenses + cb(data) + end) + else + cb(data) + end + end + + if Config.EnableESXIdentity then + data.dob = xTarget.get and xTarget.get('dateofbirth') or nil + data.height = xTarget.get and xTarget.get('height') or nil + local sex = xTarget.get and xTarget.get('sex') or nil + data.sex = (sex == 'm' or sex == 0) and 'male' or 'female' + end + + if GetResourceState('esx_status') == 'started' then + TriggerEvent('esx_status:getStatus', target, 'drunk', function(status) + if status then data.drunk = ESX.Math.Round(status.percent) end + finish() + end) + else + finish() + end end) -local fineList = {} +local fineCache = {} ESX.RegisterServerCallback('esx_policejob:getFineList', function(source, cb, category) - if not fineList[category] then - MySQL.query('SELECT * FROM fine_types WHERE category = ?', {category}, - function(fines) - fineList[category] = fines - - cb(fines) - end) - else - cb(fineList[category]) - end + category = safeNumber(category, 0) + if fineCache[category] then return cb(fineCache[category]) end + if not MySQL or not MySQL.query then + fineCache[category] = {} + return cb({}) + end + MySQL.query('SELECT id,label,amount FROM fine_types WHERE category = ?', { category }, function(rows) + fineCache[category] = rows or {} + cb(fineCache[category]) + end) end) - ESX.RegisterServerCallback('esx_policejob:getVehicleInfos', function(source, cb, plate) - local retrivedInfo = { - plate = plate - } - if Config.EnableESXIdentity then - MySQL.single('SELECT users.firstname, users.lastname FROM owned_vehicles JOIN users ON owned_vehicles.owner = users.identifier WHERE plate = ?', {plate}, - function(result) - if result then - retrivedInfo.owner = ('%s %s'):format(result.firstname, result.lastname) - end - cb(retrivedInfo) - end) - else - MySQL.scalar('SELECT owner FROM owned_vehicles WHERE plate = ?', {plate}, - function(owner) - if owner then - local xPlayer = ESX.Player(owner) - if xPlayer then - retrivedInfo.owner = xPlayer.getName() - end - end - cb(retrivedInfo) - end) - end + plate = tostring(plate or ''):upper() + local info = { plate = plate, owner = false } + if not MySQL or not MySQL.query then return cb(info) end + + if Config.EnableESXIdentity then + MySQL.single('SELECT users.firstname, users.lastname FROM owned_vehicles JOIN users ON owned_vehicles.owner = users.identifier WHERE plate = ?', { plate }, function(result) + if result then info.owner = ('%s %s'):format(result.firstname, result.lastname) end + cb(info) + end) + else + MySQL.single('SELECT owner FROM owned_vehicles WHERE plate = ?', { plate }, function(result) + if result and result.owner then + local xOwner = ESX.GetPlayerFromIdentifier and ESX.GetPlayerFromIdentifier(result.owner) or nil + info.owner = (xOwner and (xOwner.getName and xOwner.getName())) or false + end + cb(info) + end) + end end) ESX.RegisterServerCallback('esx_policejob:getArmoryWeapons', function(source, cb) - TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store) - local weapons = store.get('weapons') - - if weapons == nil then - weapons = {} - end - - cb(weapons) - end) + if GetResourceState('esx_datastore') ~= 'started' then return cb({}) end + TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store) + local weapons = store.get('weapons') + if weapons == nil then weapons = {} end + cb(weapons) + end) end) ESX.RegisterServerCallback('esx_policejob:addArmoryWeapon', function(source, cb, weaponName, removeWeapon) - local xPlayer = ESX.Player(source) - - if removeWeapon then - xPlayer.removeWeapon(weaponName) - end - - TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store) - local weapons = store.get('weapons') or {} - local foundWeapon = false - - for i=1, #weapons, 1 do - if weapons[i].name == weaponName then - weapons[i].count = weapons[i].count + 1 - foundWeapon = true - break - end - end - - if not foundWeapon then - table.insert(weapons, { - name = weaponName, - count = 1 - }) - end - - store.set('weapons', weapons) - cb() - end) + if GetResourceState('esx_datastore') ~= 'started' then return cb() end + local xPlayer = getXPlayer(source) + if removeWeapon then xPlayer.removeWeapon(weaponName) end + TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store) + local weapons = store.get('weapons') or {} + local found = false + for i=1,#weapons do + if weapons[i].name == weaponName then + weapons[i].count = (weapons[i].count or 0) + 1 + found = true + break + end + end + if not found then weapons[#weapons+1] = { name = weaponName, count = 1 } end + store.set('weapons', weapons) + cb() + end) end) ESX.RegisterServerCallback('esx_policejob:removeArmoryWeapon', function(source, cb, weaponName) - local xPlayer = ESX.Player(source) - xPlayer.addWeapon(weaponName, 500) - - TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store) - local weapons = store.get('weapons') or {} - - local foundWeapon = false - - for i=1, #weapons, 1 do - if weapons[i].name == weaponName then - weapons[i].count = (weapons[i].count > 0 and weapons[i].count - 1 or 0) - foundWeapon = true - break - end - end - - if not foundWeapon then - table.insert(weapons, { - name = weaponName, - count = 0 - }) - end - - store.set('weapons', weapons) - cb() - end) + if GetResourceState('esx_datastore') ~= 'started' then + local x = getXPlayer(source); if x then x.addWeapon(weaponName, 250) end + return cb() + end + local xPlayer = getXPlayer(source) + xPlayer.addWeapon(weaponName, 500) + TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store) + local weapons = store.get('weapons') or {} + local found = false + for i=1,#weapons do + if weapons[i].name == weaponName then + weapons[i].count = math.max(0, (weapons[i].count or 0) - 1) + found = true + break + end + end + if not found then weapons[#weapons+1] = { name = weaponName, count = 0 } end + store.set('weapons', weapons) + cb() + end) end) -ESX.RegisterServerCallback('esx_policejob:buyWeapon', function(source, cb, weaponName, type, componentNum) - local xPlayer = ESX.Player(source) - local authorizedWeapons, selectedWeapon = Config.AuthorizedWeapons[xPlayer.getJob().grade_name] - - for k,v in ipairs(authorizedWeapons) do - if v.weapon == weaponName then - selectedWeapon = v - break - end - end - - if not selectedWeapon then - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Buy Invalid Weapon - ^5%s^7!'):format(source, weaponName)) - cb(false) - else - -- Weapon - if type == 1 then - if xPlayer.getMoney() >= selectedWeapon.price then - xPlayer.removeMoney(selectedWeapon.price, "Weapon Bought") - xPlayer.addWeapon(weaponName, 100) - - cb(true) - else - cb(false) - end - - -- Weapon Component - elseif type == 2 then - local price = selectedWeapon.components[componentNum] - local weaponNum, weapon = ESX.GetWeapon(weaponName) - local component = weapon.components[componentNum] - - if component then - if xPlayer.getMoney() >= price then - xPlayer.removeMoney(price, "Weapon Component Bought") - xPlayer.addWeaponComponent(weaponName, component.name) - - cb(true) - else - cb(false) - end - else - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Buy Invalid Weapon Component - ^5%s^7!'):format(source, componentNum)) - cb(false) - end - end - end +ESX.RegisterServerCallback('esx_policejob:buyWeapon', function(source, cb, weaponName, buyType, componentNum) + local xPlayer = getXPlayer(source) + if not xPlayer then cb(false) return end + + local job = getJob(xPlayer) + if not job or job.name ~= JOB_NAME then cb(false) return end + + local gradeName = job.grade_name or job.grade + local authList = Config.AuthorizedWeapons and Config.AuthorizedWeapons[gradeName] or nil + if not authList then cb(false) return end + + local selected + for _,v in ipairs(authList) do + if v.weapon == weaponName then selected = v break end + end + if not selected then + warnExploit(xPlayer, ('Buy invalid weapon %s'):format(weaponName)) + cb(false); return + end + + local price = 0 + local account = getPaymentAccount() + + if buyType == 1 then + price = safeNumber(selected.price or 0, 0) + if price < 0 then price = 0 end + + if price > 0 then + local acc = xPlayer.getAccount(account) + local balance = acc and acc.money or 0 + if balance < price then cb(false) return end + xPlayer.removeAccountMoney(account, price, "Weapon Bought") + end + + if not playerHasWeapon(xPlayer, weaponName) then + xPlayer.addWeapon(weaponName, 100) + end + cb(true) + return + + elseif buyType == 2 then + local compPrice = selected.components and selected.components[componentNum] or 0 + price = safeNumber(compPrice, 0) + if price < 0 then price = 0 end + + local _, weaponDef = ESX.GetWeapon(weaponName) + local component = (weaponDef and weaponDef.components and weaponDef.components[componentNum]) or nil + if not component then + warnExploit(xPlayer, ('Buy invalid component #%s for %s'):format(tostring(componentNum), weaponName)) + cb(false) return + end + + if price > 0 then + local acc = xPlayer.getAccount(account) + local balance = acc and acc.money or 0 + if balance < price then cb(false) return end + xPlayer.removeAccountMoney(account, price, "Weapon Component Bought") + end + + if xPlayer.addWeaponComponent then + xPlayer.addWeaponComponent(weaponName, component.name) + else + TriggerClientEvent('esx:addWeaponComponent', source, weaponName, component.name) + end + + cb(true) + return + end + + cb(false) end) -ESX.RegisterServerCallback('esx_policejob:buyJobVehicle', function(source, cb, vehicleProps, type) - local xPlayer = ESX.Player(source) - local job = xPlayer.getJob() - local price = getPriceFromHash(vehicleProps.model, job.grade_name, type) - - -- vehicle model not found - if price == 0 then - print(('[^3WARNING^7] Player ^5%s^7 Attempted To Buy Invalid Vehicle - ^5%s^7!'):format(source, vehicleProps.model)) - cb(false) - else - if xPlayer.getMoney() >= price then - xPlayer.removeMoney(price, "Job Vehicle Bought") - - MySQL.insert('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (?, ?, ?, ?, ?, ?)', { xPlayer.getIdentifier(), json.encode(vehicleProps), vehicleProps.plate, type, job.name, true}, - function (rowsChanged) - cb(true) - end) - else - cb(false) - end - end +local function getPriceFromHash(vehicleHash, jobGrade, typeName) + local list = (Config.AuthorizedVehicles[typeName] and Config.AuthorizedVehicles[typeName][jobGrade]) or {} + for i=1, #list do + if GetHashKey(list[i].model) == vehicleHash then + return safeNumber(list[i].price, 0) + end + end + return 0 +end + +ESX.RegisterServerCallback('esx_policejob:buyJobVehicle', function(source, cb, vehicleProps, typeName) + local xPlayer = getXPlayer(source); if not xPlayer then cb(false) return end + local job = getJob(xPlayer); if not job then cb(false) return end + local price = getPriceFromHash(vehicleProps.model, job.grade_name or job.grade, typeName) + + if price <= 0 then + warnExploit(xPlayer, ('Buy invalid vehicle %s'):format(tostring(vehicleProps.model))) + cb(false); return + end + + local account = getPaymentAccount() + local acc = xPlayer.getAccount(account) + local balance = acc and acc.money or 0 + if balance < price then cb(false) return end + + xPlayer.removeAccountMoney(account, price, "Job Vehicle Bought") + + if MySQL and MySQL.insert then + MySQL.insert('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (?, ?, ?, ?, ?, ?)', + { getIdentifier(xPlayer), json.encode(vehicleProps), vehicleProps.plate, typeName, job.name, true }, + function() cb(true) end + ) + else + cb(true) + end end) ESX.RegisterServerCallback('esx_policejob:storeNearbyVehicle', function(source, cb, plates) - local xPlayer = ESX.Player(source) - local job = xPlayer.getJob() - local identifier = xPlayer.getIdentifier() - local plate = MySQL.scalar.await('SELECT plate FROM owned_vehicles WHERE owner = ? AND plate IN (?) AND job = ?', {identifier, plates, job.name}) - - if plate then - MySQL.update('UPDATE owned_vehicles SET `stored` = true WHERE owner = ? AND plate = ? AND job = ?', {identifier, plate, job.name}, - function(rowsChanged) - if rowsChanged == 0 then - cb(false) - else - cb(plate) - end - end) - else - cb(false) - end + local xPlayer = getXPlayer(source); if not xPlayer then cb(false) return end + local job = getJob(xPlayer); if not job then cb(false) return end + if not MySQL or not MySQL.scalar or not MySQL.update then cb(false) return end + + local plate = MySQL.scalar.await('SELECT plate FROM owned_vehicles WHERE owner = ? AND plate IN (?) AND job = ?', + { getIdentifier(xPlayer), plates, job.name }) + + if plate then + MySQL.update('UPDATE owned_vehicles SET `stored` = true WHERE owner = ? AND plate = ? AND job = ?', + { getIdentifier(xPlayer), plate, job.name }, + function(rowsChanged) + if rowsChanged == 0 then cb(false) else cb(plate) end + end + ) + else + cb(false) + end end) -function getPriceFromHash(vehicleHash, jobGrade, type) - local vehicles = Config.AuthorizedVehicles[type]?[jobGrade] or {} - - for i = 1, #vehicles do - local vehicle = vehicles[i] - if GetHashKey(vehicle.model) == vehicleHash then - return vehicle.price - end - end - - return 0 -end - ESX.RegisterServerCallback('esx_policejob:getStockItems', function(source, cb) - TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory) - cb(inventory.items) - end) + if GetResourceState('esx_addoninventory') ~= 'started' then return cb({}) end + TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory) + cb(inventory.items or {}) + end) end) ESX.RegisterServerCallback('esx_policejob:getPlayerInventory', function(source, cb) - local xPlayer = ESX.Player(source) - local items = xPlayer.getInventory(false) - - cb({items = items}) + local xPlayer = getXPlayer(source) + if not xPlayer then return cb({ items = {} }) end + local items = xPlayer.getInventory and xPlayer.getInventory(false) or {} + cb({ items = items }) end) AddEventHandler('onResourceStop', function(resource) - if resource == GetCurrentResourceName() then - TriggerEvent('esx_phone:removeNumber', 'police') - end + if resource == GetCurrentResourceName() then + if GetResourceState('esx_phone') == 'started' then + TriggerEvent('esx_phone:removeNumber', 'police') + end + end end) \ No newline at end of file diff --git a/[esx_addons]/esx_policejob/config.lua b/[esx_addons]/esx_policejob/shared/config.lua similarity index 96% rename from [esx_addons]/esx_policejob/config.lua rename to [esx_addons]/esx_policejob/shared/config.lua index f16269b9..a8076380 100644 --- a/[esx_addons]/esx_policejob/config.lua +++ b/[esx_addons]/esx_policejob/shared/config.lua @@ -10,6 +10,7 @@ Config.EnableArmoryManagement = false Config.EnableESXIdentity = true -- Enable if you're using esx_identity. Config.EnableESXOptionalneeds = false -- Enable if you're using esx_optionalneeds Config.EnableLicenses = false -- Enable if you're using esx_license. +Config.ArmoryAccount = 'money' -- change 'money' to 'bank' if you want armory to be funded from society bank account Config.EnableHandcuffTimer = true -- Enable handcuff timer? will unrestrain player after the time ends. Config.HandcuffTimer = 10 * 60000 -- 10 minutes. @@ -21,7 +22,7 @@ Config.MaxInService = -1 -- How many people can be in service at o Config.EnableFinePresets = false -- Set to false to use a custom input fields for fines -Config.Locale = GetConvar('esx:locale', 'en') +Config.Locale = GetConvar('esx:locale', 'fr') Config.OxInventory = ESX.GetConfig().OxInventory @@ -85,6 +86,10 @@ Config.PoliceStations = { } +Config.Cuffs = { + FreezePlayer = false +} + Config.AuthorizedWeapons = { recruit = { {weapon = 'WEAPON_APPISTOL', components = {0, 0, 1000, 4000, nil}, price = 10000}, @@ -186,7 +191,7 @@ Config.CustomPeds = { } } --- CHECK SKINCHANGER CLIENT MAIN.LUA for matching elements +-- CHECK SKINCHANGER CLIENT MAIN.LUA for matching elements (line 66) Config.Uniforms = { recruit = { male = { diff --git a/[esx_addons]/esx_policejob/shared/constants.lua b/[esx_addons]/esx_policejob/shared/constants.lua new file mode 100644 index 00000000..c83f6bab --- /dev/null +++ b/[esx_addons]/esx_policejob/shared/constants.lua @@ -0,0 +1,5 @@ +local CONST = { + JOB_NAME = 'police' +} + +return CONST \ No newline at end of file diff --git a/[esx_addons]/esx_policejob/shared/permissions.lua b/[esx_addons]/esx_policejob/shared/permissions.lua new file mode 100644 index 00000000..4132fe4c --- /dev/null +++ b/[esx_addons]/esx_policejob/shared/permissions.lua @@ -0,0 +1,17 @@ +-- Here you can set permissions for each rank of the "police" profession +-- The available permissions are: canCuff, canSearch, canFine, canJail, canOpenBossMenu +-- true means the rank has permission, false means it doesn't + +return { + jobs = { + police = { + grades = { + [0] = { label = 'Cadet', canCuff = true, canSearch = true, canFine = false, canJail = false, canOpenBossMenu = false }, + [1] = { label = 'Officer', canCuff = true, canSearch = true, canFine = true, canJail = false, canOpenBossMenu = false }, + [2] = { label = 'Sergeant', canCuff = true, canSearch = true, canFine = true, canJail = true, canOpenBossMenu = false }, + [3] = { label = 'Lieutenant', canCuff = true, canSearch = true, canFine = true, canJail = true, canOpenBossMenu = false }, + [4] = { label = 'Chief', canCuff = true, canSearch = true, canFine = true, canJail = true, canOpenBossMenu = true }, + } + } + } +} From 95b6ef513436bbfc0d335f10b07a67a65c3b776f Mon Sep 17 00:00:00 2001 From: antho Date: Mon, 6 Oct 2025 23:59:09 +0200 Subject: [PATCH 2/2] refactor: restructure client --- [esx_addons]/esx_policejob/client/blips.lua | 30 + [esx_addons]/esx_policejob/client/boot.lua | 35 + [esx_addons]/esx_policejob/client/cuffs.lua | 228 +++ [esx_addons]/esx_policejob/client/inputs.lua | 74 + .../client/interactions/identity.lua | 126 ++ .../client/interactions/search.lua | 62 + [esx_addons]/esx_policejob/client/main.lua | 1470 ----------------- [esx_addons]/esx_policejob/client/markers.lua | 209 +++ .../esx_policejob/client/menus/actions.lua | 163 ++ .../esx_policejob/client/menus/armory.lua | 278 ++++ .../esx_policejob/client/menus/cloakroom.lua | 139 ++ .../esx_policejob/client/menus/fines.lua | 90 + [esx_addons]/esx_policejob/client/objects.lua | 13 + [esx_addons]/esx_policejob/client/phone.lua | 19 + [esx_addons]/esx_policejob/client/state.lua | 21 + [esx_addons]/esx_policejob/client/utils.lua | 51 + [esx_addons]/esx_policejob/fxmanifest.lua | 19 +- 17 files changed, 1553 insertions(+), 1474 deletions(-) create mode 100644 [esx_addons]/esx_policejob/client/blips.lua create mode 100644 [esx_addons]/esx_policejob/client/boot.lua create mode 100644 [esx_addons]/esx_policejob/client/cuffs.lua create mode 100644 [esx_addons]/esx_policejob/client/inputs.lua create mode 100644 [esx_addons]/esx_policejob/client/interactions/identity.lua create mode 100644 [esx_addons]/esx_policejob/client/interactions/search.lua delete mode 100644 [esx_addons]/esx_policejob/client/main.lua create mode 100644 [esx_addons]/esx_policejob/client/markers.lua create mode 100644 [esx_addons]/esx_policejob/client/menus/actions.lua create mode 100644 [esx_addons]/esx_policejob/client/menus/armory.lua create mode 100644 [esx_addons]/esx_policejob/client/menus/cloakroom.lua create mode 100644 [esx_addons]/esx_policejob/client/menus/fines.lua create mode 100644 [esx_addons]/esx_policejob/client/objects.lua create mode 100644 [esx_addons]/esx_policejob/client/phone.lua create mode 100644 [esx_addons]/esx_policejob/client/state.lua create mode 100644 [esx_addons]/esx_policejob/client/utils.lua diff --git a/[esx_addons]/esx_policejob/client/blips.lua b/[esx_addons]/esx_policejob/client/blips.lua new file mode 100644 index 00000000..71032f6e --- /dev/null +++ b/[esx_addons]/esx_policejob/client/blips.lua @@ -0,0 +1,30 @@ +local A = POLICE + +function createBlip(id) + local ped = GetPlayerPed(id) + local blip = GetBlipFromEntity(ped) + if not DoesBlipExist(blip) then + blip = AddBlipForEntity(ped) + SetBlipSprite(blip, 1) + ShowHeadingIndicatorOnBlip(blip, true) + SetBlipRotation(blip, math.ceil(GetEntityHeading(ped))) + SetBlipNameToPlayerName(blip, id) + SetBlipScale(blip, 0.85) + SetBlipAsShortRange(blip, true) + table.insert(A.blipsCops, blip) + end +end + +CreateThread(function() + for _, v in pairs(Config.PoliceStations) do + local blip = AddBlipForCoord(v.Blip.Coords) + SetBlipSprite (blip, v.Blip.Sprite) + SetBlipDisplay(blip, v.Blip.Display) + SetBlipScale (blip, v.Blip.Scale) + SetBlipColour (blip, v.Blip.Colour) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName('STRING') + AddTextComponentSubstringPlayerName(TranslateCap('map_blip')) + EndTextCommandSetBlipName(blip) + end +end) diff --git a/[esx_addons]/esx_policejob/client/boot.lua b/[esx_addons]/esx_policejob/client/boot.lua new file mode 100644 index 00000000..2f64dec2 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/boot.lua @@ -0,0 +1,35 @@ +local A = POLICE + +AddEventHandler('esx:onPlayerSpawn', function() + A.isDead = false + TriggerEvent('esx_policejob:unrestrain') + if not A.hasAlreadyJoined then + TriggerServerEvent('esx_policejob:spawned') + end + A.hasAlreadyJoined = true +end) + +AddEventHandler('esx:onPlayerDeath', function() + A.isDead = true +end) + +AddEventHandler('onResourceStop', function(resource) + if resource == GetCurrentResourceName() then + TriggerEvent('esx_policejob:unrestrain') + TriggerEvent('esx_phone:removeSpecialContact', 'police') + + if Config.EnableESXService then + TriggerServerEvent('esx_service:disableService', 'police') + end + + if Config.EnableHandcuffTimer and A.handcuffTimer.active then + ESX.ClearTimeout(A.handcuffTimer.task) + end + end +end) + +function ImpoundVehicle(vehicle) + ESX.Game.DeleteVehicle(vehicle) + ESX.ShowNotification(TranslateCap('impound_successful')) + A.currentTask.busy = false +end diff --git a/[esx_addons]/esx_policejob/client/cuffs.lua b/[esx_addons]/esx_policejob/client/cuffs.lua new file mode 100644 index 00000000..17335987 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/cuffs.lua @@ -0,0 +1,228 @@ +local A = POLICE + +RegisterNetEvent('esx_policejob:handcuff') +AddEventHandler('esx_policejob:handcuff', function() + A.isHandcuffed = not A.isHandcuffed + local playerPed = PlayerPedId() + + if A.isHandcuffed then + POLICE.playCuffAnim(playerPed) + + SetEnableHandcuffs(playerPed, true) + DisablePlayerFiring(playerPed, true) + SetCurrentPedWeapon(playerPed, `WEAPON_UNARMED`, true) + SetPedCanPlayGestureAnims(playerPed, false) + + if Config.Cuffs and Config.Cuffs.FreezePlayer then + FreezeEntityPosition(playerPed, true) + else + FreezeEntityPosition(playerPed, false) + SetPedCanRagdoll(playerPed, true) + SetPedMoveRateOverride(playerPed, 1.0) + SetRunSprintMultiplierForPlayer(PlayerId(), 1.0) + end + + DisplayRadar(false) + SetEntityCollision(playerPed, true, true) + SetEntityDynamic(playerPed, true) + TriggerServerEvent('esx_policejob:cuffsState', true) + + if Config.EnableHandcuffTimer then + if A.handcuffTimer.active then ESX.ClearTimeout(A.handcuffTimer.task) end + StartHandcuffTimer() + end + else + if Config.EnableHandcuffTimer and A.handcuffTimer.active then + ESX.ClearTimeout(A.handcuffTimer.task) + end + + POLICE.stopCuffAnim(playerPed) + SetEnableHandcuffs(playerPed, false) + DisablePlayerFiring(playerPed, false) + SetPedCanPlayGestureAnims(playerPed, true) + FreezeEntityPosition(playerPed, false) + DisplayRadar(true) + SetEntityCollision(playerPed, true, true) + SetEntityDynamic(playerPed, true) + + TriggerServerEvent('esx_policejob:cuffsState', false) + end +end) + +RegisterNetEvent('esx_policejob:unrestrain') +AddEventHandler('esx_policejob:unrestrain', function() + if not A.isHandcuffed then return end + local playerPed = PlayerPedId() + A.isHandcuffed = false + + POLICE.stopCuffAnim(playerPed) + SetEnableHandcuffs(playerPed, false) + DisablePlayerFiring(playerPed, false) + SetPedCanPlayGestureAnims(playerPed, true) + FreezeEntityPosition(playerPed, false) + DisplayRadar(true) + + if Config.EnableHandcuffTimer and A.handcuffTimer.active then + ESX.ClearTimeout(A.handcuffTimer.task) + end + + TriggerServerEvent('esx_policejob:cuffsState', false) +end) + +CreateThread(function() + while true do + if A.isHandcuffed and (not Config.Cuffs or not Config.Cuffs.FreezePlayer) then + local ped = PlayerPedId() + + if IsPedUsingAnyScenario(ped) then + ClearPedTasksImmediately(ped) + end + + if IsEntityPlayingAnim(ped, A.ARREST_DICT, A.ARREST_ANIM, 3) ~= 1 then + POLICE.playCuffAnim(ped) + end + + FreezeEntityPosition(ped, false) + + DisableControlAction(0, 24, true) + DisableControlAction(0, 25, true) + DisableControlAction(0, 37, true) + DisableControlAction(0, 140, true) + DisableControlAction(0, 141, true) + DisableControlAction(0, 142, true) + DisableControlAction(0, 47, true) + DisableControlAction(2, 36, true) + Wait(0) + else + Wait(300) + end + end +end) + +RegisterNetEvent('esx_policejob:drag') +AddEventHandler('esx_policejob:drag', function(copId) + if A.isHandcuffed then + A.dragStatus.isDragged = not A.dragStatus.isDragged + A.dragStatus.CopId = copId + end +end) + +CreateThread(function() + local wasDragged + while true do + local sleep = 1500 + if A.isHandcuffed and A.dragStatus.isDragged then + sleep = 50 + local targetPed = GetPlayerPed(GetPlayerFromServerId(A.dragStatus.CopId)) + if DoesEntityExist(targetPed) and IsPedOnFoot(targetPed) and not Player(A.dragStatus.CopId).state.isDead then + if not wasDragged then + AttachEntityToEntity(PlayerPedId(), targetPed, 11816, 0.54, 0.54, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) + wasDragged = true + else + Wait(1000) + end + else + wasDragged = false + A.dragStatus.isDragged = false + DetachEntity(PlayerPedId(), true, false) + end + elseif wasDragged then + wasDragged = false + DetachEntity(PlayerPedId(), true, false) + end + Wait(sleep) + end +end) + +RegisterNetEvent('esx_policejob:putInVehicle') +AddEventHandler('esx_policejob:putInVehicle', function() + if A.isHandcuffed then + local playerPed = PlayerPedId() + local vehicle, distance = ESX.Game.GetClosestVehicle() + if vehicle and distance < 5.0 then + local maxSeats, freeSeat = GetVehicleMaxNumberOfPassengers(vehicle) + for i=maxSeats - 1, 0, -1 do + if IsVehicleSeatFree(vehicle, i) then freeSeat = i break end + end + if freeSeat then + TaskWarpPedIntoVehicle(playerPed, vehicle, freeSeat) + A.dragStatus.isDragged = false + end + end + end +end) + +RegisterNetEvent('esx_policejob:OutVehicle') +AddEventHandler('esx_policejob:OutVehicle', function() + local ped = PlayerPedId() + if IsPedSittingInAnyVehicle(ped) then + local vehicle = GetVehiclePedIsIn(ped, false) + TaskLeaveVehicle(ped, vehicle, 64) + end +end) + +CreateThread(function() + while true do + local sleep = 1000 + if A.isHandcuffed and Config.Cuffs and Config.Cuffs.FreezePlayer then + sleep = 0 + DisableControlAction(0, 1, true) + DisableControlAction(0, 2, true) + DisableControlAction(0, 24, true) + DisableControlAction(0, 257, true) + DisableControlAction(0, 25, true) + DisableControlAction(0, 263, true) + DisableControlAction(0, 32, true) + DisableControlAction(0, 34, true) + DisableControlAction(0, 31, true) + DisableControlAction(0, 30, true) + DisableControlAction(0, 45, true) + DisableControlAction(0, 22, true) + DisableControlAction(0, 44, true) + DisableControlAction(0, 37, true) + DisableControlAction(0, 23, true) + DisableControlAction(0, 288, true) + DisableControlAction(0, 289, true) + DisableControlAction(0, 170, true) + DisableControlAction(0, 167, true) + DisableControlAction(0, 0, true) + DisableControlAction(0, 26, true) + DisableControlAction(0, 73, true) + DisableControlAction(2, 199, true) + DisableControlAction(0, 59, true) + DisableControlAction(0, 71, true) + DisableControlAction(0, 72, true) + DisableControlAction(2, 36, true) + DisableControlAction(0, 47, true) + DisableControlAction(0, 264, true) + DisableControlAction(0, 257, true) + DisableControlAction(0, 140, true) + DisableControlAction(0, 141, true) + DisableControlAction(0, 142, true) + DisableControlAction(0, 143, true) + DisableControlAction(0, 75, true) + DisableControlAction(27, 75, true) + + local ped = PlayerPedId() + if IsEntityPlayingAnim(ped, 'mp_arresting', 'idle', 3) ~= 1 then + ESX.Streaming.RequestAnimDict('mp_arresting', function() + TaskPlayAnim(ped, 'mp_arresting', 'idle', 8.0, -8.0, -1, 49, 0.0, false, false, false) + RemoveAnimDict('mp_arresting') + end) + end + end + Wait(sleep) + end +end) + +function StartHandcuffTimer() + if Config.EnableHandcuffTimer and A.handcuffTimer.active then + ESX.ClearTimeout(A.handcuffTimer.task) + end + A.handcuffTimer.active = true + A.handcuffTimer.task = ESX.SetTimeout(Config.HandcuffTimer, function() + ESX.ShowNotification(TranslateCap('unrestrained_timer')) + TriggerEvent('esx_policejob:unrestrain') + A.handcuffTimer.active = false + end) +end diff --git a/[esx_addons]/esx_policejob/client/inputs.lua b/[esx_addons]/esx_policejob/client/inputs.lua new file mode 100644 index 00000000..d48827c6 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/inputs.lua @@ -0,0 +1,74 @@ +local A = POLICE + +ESX.RegisterInput("police:interact", "(ESX PoliceJob) " .. TranslateCap('interaction'), "keyboard", "E", function() + if not A.CurrentAction then return end + if (not ESX.PlayerData.job) or (ESX.PlayerData.job.name ~= 'police') then + return + end + + if A.CurrentAction == 'menu_cloakroom' then + OpenCloakroomMenu() + + elseif A.CurrentAction == 'menu_armory' then + if not Config.EnableESXService or A.playerInService then + OpenArmoryMenu(A.CurrentActionData.station) + else + ESX.ShowNotification(TranslateCap('service_not')) + end + + elseif A.CurrentAction == 'menu_vehicle_spawner' then + if not Config.EnableESXService or A.playerInService then + OpenVehicleSpawnerMenu('car', A.CurrentActionData.station, A.CurrentActionData.part, A.CurrentActionData.partNum) + else + ESX.ShowNotification(TranslateCap('service_not')) + end + + elseif A.CurrentAction == 'Helicopters' then + if not Config.EnableESXService or A.playerInService then + OpenVehicleSpawnerMenu('helicopter', A.CurrentActionData.station, A.CurrentActionData.part, A.CurrentActionData.partNum) + else + ESX.ShowNotification(TranslateCap('service_not')) + end + + elseif A.CurrentAction == 'delete_vehicle' then + ESX.Game.DeleteVehicle(A.CurrentActionData.vehicle) + + elseif A.CurrentAction == 'menu_boss_actions' then + ESX.CloseContext() + TriggerEvent('esx_society:openBossMenu', 'police', function() + ESX.CloseContext() + A.CurrentAction = 'menu_boss_actions' + A.CurrentActionMsg = TranslateCap('open_bossmenu') + A.CurrentActionData = {} + end, { wash = false }) + + elseif A.CurrentAction == 'remove_entity' then + if A.CurrentActionData and A.CurrentActionData.entity and DoesEntityExist(A.CurrentActionData.entity) then + DeleteEntity(A.CurrentActionData.entity) + end + end + + A.CurrentAction = nil +end) + +ESX.RegisterInput("police:quickactions", "(ESX PoliceJob) "..TranslateCap('quick_actions'), "keyboard", "F6", function() + if not ESX.PlayerData.job or (ESX.PlayerData.job.name ~= 'police') or A.isDead then + return + end + if not Config.EnableESXService or A.playerInService then + OpenPoliceActionsMenu() + else + ESX.ShowNotification(TranslateCap('service_not')) + end +end) + +CreateThread(function() + while true do + local Sleep = 1000 + if A.CurrentAction then + Sleep = 0 + ESX.ShowHelpNotification(A.CurrentActionMsg) + end + Wait(Sleep) + end +end) diff --git a/[esx_addons]/esx_policejob/client/interactions/identity.lua b/[esx_addons]/esx_policejob/client/interactions/identity.lua new file mode 100644 index 00000000..157ab9b0 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/interactions/identity.lua @@ -0,0 +1,126 @@ +local A = POLICE + +function OpenIdentityCardMenu(player) + ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) + local elements = { + {icon = "fas fa-user", title = TranslateCap('name', data.name)}, + {icon = "fas fa-user", title = TranslateCap('job', ('%s - %s'):format(data.job, data.grade))} + } + + if Config.EnableESXIdentity then + elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('sex', TranslateCap(data.sex))} + elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('dob', data.dob)} + elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('height', data.height)} + end + + if Config.EnableESXOptionalneeds and data.drunk then + elements[#elements+1] = {icon = "fas fa-wine-bottle", title = TranslateCap('bac', data.drunk)} + end + + if data.licenses then + elements[#elements+1] = {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_label')} + for i=1, #data.licenses do + elements[#elements+1] = {icon = "fas fa-scroll", title = data.licenses[i].label} + end + end + + ESX.OpenContext("right", elements, nil, function(menu) + OpenPoliceActionsMenu() + end) + end, GetPlayerServerId(player)) +end + +function ShowPlayerLicense(player) + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_revoke')} + } + + ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(playerData) + if playerData.licenses then + for i=1, #playerData.licenses do + local lic = playerData.licenses[i] + if lic.label and lic.type then + elements[#elements+1] = { + icon = "fas fa-scroll", + title = lic.label, + type = lic.type + } + end + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + ESX.ShowNotification(TranslateCap('licence_you_revoked', data.current.title, playerData.name)) + TriggerServerEvent('esx_policejob:message', GetPlayerServerId(player), TranslateCap('license_revoked', data.current.title)) + TriggerServerEvent('esx_license:removeLicense', GetPlayerServerId(player), data.current.type) + ESX.SetTimeout(300, function() + ShowPlayerLicense(player) + end) + end) + end, GetPlayerServerId(player)) +end + +function OpenUnpaidBillsMenu(player) + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('unpaid_bills')} + } + + ESX.TriggerServerCallback('esx_billing:getTargetBills', function(bills) + for _,bill in ipairs(bills) do + elements[#elements+1] = { + unselectable = true, + icon = "fas fa-scroll", + title = ('%s - %s'):format(bill.label, TranslateCap('armory_item', ESX.Math.GroupDigits(bill.amount))), + billId = bill.id + } + end + ESX.OpenContext("right", elements, nil, nil) + end, GetPlayerServerId(player)) +end + +function OpenVehicleInfosMenu(vehicleData) + ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) + local elements = { + {unselectable = true, icon = "fas fa-car", title = TranslateCap('vehicle_info')}, + {icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} + } + if not retrivedInfo.owner then + elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} + else + elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} + end + ESX.OpenContext("right", elements, nil, nil) + end, vehicleData.plate) +end + +function LookupVehicle(elementF) + local elements = { + {unselectable = true, icon = "fas fa-car", title = elementF.title}, + {title = TranslateCap('search_plate'), input = true, inputType = "text", inputPlaceholder = "ABC 123"}, + {icon = "fas fa-check-double", title = TranslateCap('lookup_plate'), value = "lookup"} + } + + ESX.OpenContext("right", elements, function(menu,element) + local data = {value = menu.eles[2].inputValue} + local length = data.value and string.len(data.value) or 0 + if not data.value or length < 2 or length > 8 then + ESX.ShowNotification(TranslateCap('search_database_error_invalid')) + else + ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) + local el = { + {unselectable = true, icon = "fas fa-car", title = elementF.title}, + {unselectable = true, icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} + } + if not retrivedInfo.owner then + el[#el+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} + else + el[#el+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} + end + ESX.OpenContext("right", el, nil, function() + OpenPoliceActionsMenu() + end) + end, data.value) + end + end) +end diff --git a/[esx_addons]/esx_policejob/client/interactions/search.lua b/[esx_addons]/esx_policejob/client/interactions/search.lua new file mode 100644 index 00000000..0e8a008f --- /dev/null +++ b/[esx_addons]/esx_policejob/client/interactions/search.lua @@ -0,0 +1,62 @@ +local A = POLICE + +function OpenBodySearchMenu(player) + if Config.OxInventory then + ESX.CloseContext() + exports.ox_inventory:openInventory('player', GetPlayerServerId(player)) + return + end + + ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) + local elements = { + {unselectable = true, icon = "fas fa-user", title = TranslateCap('search')} + } + + for i=1, #data.accounts do + if data.accounts[i].name == 'black_money' and data.accounts[i].money > 0 then + elements[#elements+1] = { + icon = "fas fa-money-bill", + title = TranslateCap('confiscate_dirty', ESX.Math.Round(data.accounts[i].money)), + value = 'black_money', + itemType = 'item_account', + amount = data.accounts[i].money + } + break + end + end + + elements[#elements+1] = {unselectable = true, icon = "fas fa-gun", title = TranslateCap('guns_label')} + + for i=1, #data.weapons do + elements[#elements+1] = { + icon = "fas fa-gun", + title = TranslateCap('confiscate_weapon', ESX.GetWeaponLabel(data.weapons[i].name), data.weapons[i].ammo), + value = data.weapons[i].name, + itemType = 'item_weapon', + amount = data.weapons[i].ammo + } + end + + elements[#elements+1] = {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory_label')} + + for i=1, #data.inventory do + if data.inventory[i].count > 0 then + elements[#elements+1] = { + icon = "fas fa-box", + title = TranslateCap('confiscate_inv', data.inventory[i].count, data.inventory[i].label), + value = data.inventory[i].name, + itemType = 'item_standard', + amount = data.inventory[i].count + } + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local dataEl = {current = element} + if dataEl.current.value then + TriggerServerEvent('esx_policejob:confiscatePlayerItem', GetPlayerServerId(player), dataEl.current.itemType, dataEl.current.value, dataEl.current.amount) + OpenBodySearchMenu(player) + end + end) + end, GetPlayerServerId(player)) +end diff --git a/[esx_addons]/esx_policejob/client/main.lua b/[esx_addons]/esx_policejob/client/main.lua deleted file mode 100644 index 2c5fd98e..00000000 --- a/[esx_addons]/esx_policejob/client/main.lua +++ /dev/null @@ -1,1470 +0,0 @@ -local CurrentActionData, handcuffTimer, dragStatus, blipsCops, currentTask = {}, {}, {}, {}, {} -local HasAlreadyEnteredMarker, isDead, isHandcuffed, hasAlreadyJoined, playerInService = false, false, false, false, false -local LastStation, LastPart, LastPartNum, LastEntity, CurrentAction, CurrentActionMsg -local ARREST_DICT, ARREST_ANIM = 'mp_arresting', 'idle' -dragStatus.isDragged, isInShopMenu = false, false - -local function playCuffAnim(ped) - RequestAnimDict(ARREST_DICT) - local tries = 0 - while not HasAnimDictLoaded(ARREST_DICT) and tries < 100 do - Wait(10); tries = tries + 1 - end - TaskPlayAnim(ped, ARREST_DICT, ARREST_ANIM, 8.0, -8.0, -1, 49, 0.0, false, false, false) -end -local function stopCuffAnim(ped) - ClearPedSecondaryTask(ped) - if HasAnimDictLoaded(ARREST_DICT) then - RemoveAnimDict(ARREST_DICT) - end -end - -function cleanPlayer(playerPed) - SetPedArmour(playerPed, 0) - ClearPedBloodDamage(playerPed) - ResetPedVisibleDamage(playerPed) - ClearPedLastWeaponDamage(playerPed) - ResetPedMovementClipset(playerPed, 0) -end - -function setUniform(uniform, playerPed) - TriggerEvent('skinchanger:getSkin', function(skin) - local uniformObject - local sex = (skin.sex == 0) and "male" or "female" - - if Config.Uniforms and Config.Uniforms[uniform] and Config.Uniforms[uniform][sex] then - uniformObject = Config.Uniforms[uniform][sex] - end - - if uniformObject then - TriggerEvent('skinchanger:loadClothes', skin, uniformObject) - if uniform == 'bullet_wear' then - SetPedArmour(playerPed, 100) - end - else - ESX.ShowNotification(TranslateCap('no_outfit')) - end - end) -end - -function OpenCloakroomMenu() - local playerPed = PlayerPedId() - local grade = ESX.PlayerData.job.grade_name - - local elements = { - {unselectable = true, icon = "fas fa-shirt", title = TranslateCap("cloakroom")}, - {icon = "fas fa-shirt", title = TranslateCap('citizen_wear'), value = 'citizen_wear'}, - {icon = "fas fa-shirt", title = TranslateCap('bullet_wear'), uniform = 'bullet_wear'}, - {icon = "fas fa-shirt", title = TranslateCap('gilet_wear'), uniform = 'gilet_wear'}, - {icon = "fas fa-shirt", title = TranslateCap('police_wear'), uniform = grade} - } - - if Config.EnableCustomPeds then - if Config.CustomPeds and Config.CustomPeds.shared then - for _,v in ipairs(Config.CustomPeds.shared) do - elements[#elements+1] = { - icon = "fas fa-shirt", - title = v.label, - value = 'freemode_ped', - maleModel = v.maleModel, - femaleModel = v.femaleModel - } - end - end - if Config.CustomPeds and Config.CustomPeds[grade] then - for _,v in ipairs(Config.CustomPeds[grade]) do - elements[#elements+1] = { - icon = "fas fa-shirt", - title = v.label, - value = 'freemode_ped', - maleModel = v.maleModel, - femaleModel = v.femaleModel - } - end - end - end - - ESX.OpenContext("right", elements, function(menu,element) - cleanPlayer(playerPed) - local data = {current = element} - - if data.current.value == 'citizen_wear' then - if Config.EnableCustomPeds then - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) - local isMale = skin.sex == 0 - TriggerEvent('skinchanger:loadDefaultModel', isMale, function() - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin2) - TriggerEvent('skinchanger:loadSkin', skin2) - TriggerEvent('esx:restoreLoadout') - end) - end) - end) - else - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) - TriggerEvent('skinchanger:loadSkin', skin) - end) - end - - if Config.EnableESXService then - ESX.TriggerServerCallback('esx_service:isInService', function(isInService) - if isInService then - playerInService = false - local notification = { - title = TranslateCap('service_anonunce'), - subject = '', - msg = TranslateCap('service_out_announce', GetPlayerName(PlayerId())), - iconType = 1 - } - TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') - TriggerServerEvent('esx_service:disableService', 'police') - ESX.ShowNotification(TranslateCap('service_out')) - end - end, 'police') - end - end - - if Config.EnableESXService and data.current.value ~= 'citizen_wear' then - local awaitService - ESX.TriggerServerCallback('esx_service:isInService', function(isInService) - if not isInService then - if Config.MaxInService ~= -1 then - ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount) - if not canTakeService then - ESX.ShowNotification(TranslateCap('service_max', inServiceCount, maxInService)) - else - awaitService = true - playerInService = true - local notification = { - title = TranslateCap('service_anonunce'), - subject = '', - msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), - iconType = 1 - } - TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') - ESX.ShowNotification(TranslateCap('service_in')) - end - end, 'police') - else - awaitService = true - playerInService = true - local notification = { - title = TranslateCap('service_anonunce'), - subject = '', - msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), - iconType = 1 - } - TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') - ESX.ShowNotification(TranslateCap('service_in')) - end - else - awaitService = true - end - end, 'police') - - while awaitService == nil do - Wait(0) - end - if not awaitService then - return - end - end - - if data.current.uniform then - setUniform(data.current.uniform, playerPed) - elseif data.current.value == 'freemode_ped' then - ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) - local modelHash = skin.sex == 0 and joaat(data.current.maleModel) or joaat(data.current.femaleModel) - ESX.Streaming.RequestModel(modelHash, function() - SetPlayerModel(PlayerId(), modelHash) - SetModelAsNoLongerNeeded(modelHash) - SetPedDefaultComponentVariation(PlayerPedId()) - TriggerEvent('esx:restoreLoadout') - end) - end) - end - end, function(menu) - CurrentAction = 'menu_cloakroom' - CurrentActionMsg = TranslateCap('open_cloackroom') - CurrentActionData = {} - end) -end - -function OpenArmoryMenu(station) - if Config.OxInventory then - exports.ox_inventory:openInventory('stash', { id = 'society_police', owner = station }) - return ESX.CloseContext() - end - - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory')}, - {icon = "fas fa-gun", title = TranslateCap('buy_weapons'), value = 'buy_weapons'} - } - - if Config.EnableArmoryManagement then - table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('get_weapon'), value = 'get_weapon'}) - table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('put_weapon'), value = 'put_weapon'}) - table.insert(elements, {icon = "fas fa-box", title = TranslateCap('remove_object'), value = 'get_stock'}) - table.insert(elements, {icon = "fas fa-box", title = TranslateCap('deposit_object'), value = 'put_stock'}) - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if data.current.value == 'get_weapon' then - OpenGetWeaponMenu() - elseif data.current.value == 'put_weapon' then - OpenPutWeaponMenu() - elseif data.current.value == 'buy_weapons' then - OpenBuyWeaponsMenu() - elseif data.current.value == 'put_stock' then - OpenPutStocksMenu() - elseif data.current.value == 'get_stock' then - OpenGetStocksMenu() - end - end, function(menu) - CurrentAction = 'menu_armory' - CurrentActionMsg = TranslateCap('open_armory') - CurrentActionData = {station = station} - end) -end - -function OpenPoliceActionsMenu() - local elements = { - {unselectable = true, icon = "fas fa-police", title = TranslateCap('menu_title')}, - {icon = "fas fa-user", title = TranslateCap('citizen_interaction'), value = 'citizen_interaction'}, - {icon = "fas fa-car", title = TranslateCap('vehicle_interaction'), value = 'vehicle_interaction'}, - {icon = "fas fa-object", title = TranslateCap('object_spawner'), value = 'object_spawner'} - } - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - - if data.current.value == 'citizen_interaction' then - local elements2 = { - {unselectable = true, icon = "fas fa-user", title = element.title}, - {icon = "fas fa-idkyet", title = TranslateCap('id_card'), value = 'identity_card'}, - {icon = "fas fa-idkyet", title = TranslateCap('search'), value = 'search'}, - {icon = "fas fa-idkyet", title = TranslateCap('handcuff'), value = 'handcuff'}, - {icon = "fas fa-idkyet", title = TranslateCap('drag'), value = 'drag'}, - {icon = "fas fa-idkyet", title = TranslateCap('put_in_vehicle'), value = 'put_in_vehicle'}, - {icon = "fas fa-idkyet", title = TranslateCap('out_the_vehicle'), value = 'out_the_vehicle'}, - {icon = "fas fa-idkyet", title = TranslateCap('fine'), value = 'fine'}, - {icon = "fas fa-idkyet", title = TranslateCap('unpaid_bills'), value = 'unpaid_bills'} - } - - if Config.EnableLicenses then - elements2[#elements2+1] = { - icon = "fas fa-scroll", - title = TranslateCap('license_check'), - value = 'license' - } - end - - ESX.OpenContext("right", elements2, function(menu2,element2) - local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() - if closestPlayer ~= -1 and closestDistance <= 3.0 then - local data2 = {current = element2} - local action = data2.current.value - - if action == 'identity_card' then - OpenIdentityCardMenu(closestPlayer) - elseif action == 'search' then - OpenBodySearchMenu(closestPlayer) - elseif action == 'handcuff' then - TriggerServerEvent('esx_policejob:handcuff', GetPlayerServerId(closestPlayer)) - elseif action == 'drag' then - TriggerServerEvent('esx_policejob:drag', GetPlayerServerId(closestPlayer)) - elseif action == 'put_in_vehicle' then - TriggerServerEvent('esx_policejob:putInVehicle', GetPlayerServerId(closestPlayer)) - elseif action == 'out_the_vehicle' then - TriggerServerEvent('esx_policejob:OutVehicle', GetPlayerServerId(closestPlayer)) - elseif action == 'fine' then - OpenFineMenu(closestPlayer) - elseif action == 'license' then - ShowPlayerLicense(closestPlayer) - elseif action == 'unpaid_bills' then - OpenUnpaidBillsMenu(closestPlayer) - end - else - ESX.ShowNotification(TranslateCap('no_players_nearby')) - end - end, function(menu) - OpenPoliceActionsMenu() - end) - - elseif data.current.value == 'vehicle_interaction' then - local elements3 = { - {unselectable = true, icon = "fas fa-car", title = element.title} - } - local playerPed = PlayerPedId() - local vehicle = ESX.Game.GetVehicleInDirection() - - if DoesEntityExist(vehicle) then - elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('vehicle_info'), value = 'vehicle_infos'} - elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('pick_lock'), value = 'hijack_vehicle'} - elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('impound'), value = 'impound'} - end - - elements3[#elements3+1] = { - icon = "fas fa-scroll", - title = TranslateCap('search_database'), - value = 'search_database' - } - - ESX.OpenContext("right", elements3, function(menu3,element3) - local data2 = {current = element3} - local coords = GetEntityCoords(playerPed) - vehicle = ESX.Game.GetVehicleInDirection() - local action = data2.current.value - - if action == 'search_database' then - LookupVehicle(element3) - elseif DoesEntityExist(vehicle) then - if action == 'vehicle_infos' then - local vehicleData = ESX.Game.GetVehicleProperties(vehicle) - OpenVehicleInfosMenu(vehicleData) - elseif action == 'hijack_vehicle' then - if IsAnyVehicleNearPoint(coords.x, coords.y, coords.z, 3.0) then - TaskStartScenarioInPlace(playerPed, 'WORLD_HUMAN_WELDING', 0, true) - Wait(20000) - ClearPedTasksImmediately(playerPed) - SetVehicleDoorsLocked(vehicle, 1) - SetVehicleDoorsLockedForAllPlayers(vehicle, false) - ESX.ShowNotification(TranslateCap('vehicle_unlocked')) - end - elseif action == 'impound' then - if currentTask.busy then return end - ESX.ShowHelpNotification(TranslateCap('impound_prompt')) - TaskStartScenarioInPlace(playerPed, 'CODE_HUMAN_MEDIC_TEND_TO_DEAD', 0, true) - currentTask.busy = true - currentTask.task = ESX.SetTimeout(10000, function() - ClearPedTasks(playerPed) - ImpoundVehicle(vehicle) - Wait(100) - end) - CreateThread(function() - while currentTask.busy do - Wait(1000) - vehicle = GetClosestVehicle(coords.x, coords.y, coords.z, 3.0, 0, 71) - if not DoesEntityExist(vehicle) and currentTask.busy then - ESX.ShowNotification(TranslateCap('impound_canceled_moved')) - ESX.ClearTimeout(currentTask.task) - ClearPedTasks(playerPed) - currentTask.busy = false - break - end - end - end) - end - else - ESX.ShowNotification(TranslateCap('no_vehicles_nearby')) - end - end, function(menu) - OpenPoliceActionsMenu() - end) - - elseif data.current.value == "object_spawner" then - local elements4 = { - {unselectable = true, icon = "fas fa-object", title = element.title}, - {icon = "fas fa-cone", title = TranslateCap('cone'), model = 'prop_roadcone02a'}, - {icon = "fas fa-cone", title = TranslateCap('barrier'), model = 'prop_barrier_work05'}, - {icon = "fas fa-cone", title = TranslateCap('spikestrips'), model = 'p_ld_stinger_s'}, - {icon = "fas fa-cone", title = TranslateCap('box'), model = 'prop_boxpile_07d'}, - {icon = "fas fa-cone", title = TranslateCap('cash'), model = 'hei_prop_cash_crate_half_full'} - } - - ESX.OpenContext("right", elements4, function(menu4,element4) - local data2 = {current = element4} - local playerPed = PlayerPedId() - local coords, forward = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed) - local objectCoords = (coords + forward * 1.0) - ESX.Game.SpawnObject(data2.current.model, objectCoords, function(obj) - Wait(100) - SetEntityHeading(obj, GetEntityHeading(playerPed)) - PlaceObjectOnGroundProperly(obj) - end) - end, function(menu) - OpenPoliceActionsMenu() - end) - end - end) -end - -function OpenIdentityCardMenu(player) - ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) - local elements = { - {icon = "fas fa-user", title = TranslateCap('name', data.name)}, - {icon = "fas fa-user", title = TranslateCap('job', ('%s - %s'):format(data.job, data.grade))} - } - - if Config.EnableESXIdentity then - elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('sex', TranslateCap(data.sex))} - elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('dob', data.dob)} - elements[#elements+1] = {icon = "fas fa-user", title = TranslateCap('height', data.height)} - end - - if Config.EnableESXOptionalneeds and data.drunk then - elements[#elements+1] = {icon = "fas fa-wine-bottle", title = TranslateCap('bac', data.drunk)} - end - - if data.licenses then - elements[#elements+1] = {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_label')} - for i=1, #data.licenses do - elements[#elements+1] = {icon = "fas fa-scroll", title = data.licenses[i].label} - end - end - - ESX.OpenContext("right", elements, nil, function(menu) - OpenPoliceActionsMenu() - end) - end, GetPlayerServerId(player)) -end - -function OpenBodySearchMenu(player) - if Config.OxInventory then - ESX.CloseContext() - exports.ox_inventory:openInventory('player', GetPlayerServerId(player)) - return - end - - ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data) - local elements = { - {unselectable = true, icon = "fas fa-user", title = TranslateCap('search')} - } - - for i=1, #data.accounts do - if data.accounts[i].name == 'black_money' and data.accounts[i].money > 0 then - elements[#elements+1] = { - icon = "fas fa-money-bill", - title = TranslateCap('confiscate_dirty', ESX.Math.Round(data.accounts[i].money)), - value = 'black_money', - itemType = 'item_account', - amount = data.accounts[i].money - } - break - end - end - - elements[#elements+1] = {unselectable = true, icon = "fas fa-gun", title = TranslateCap('guns_label')} - - for i=1, #data.weapons do - elements[#elements+1] = { - icon = "fas fa-gun", - title = TranslateCap('confiscate_weapon', ESX.GetWeaponLabel(data.weapons[i].name), data.weapons[i].ammo), - value = data.weapons[i].name, - itemType = 'item_weapon', - amount = data.weapons[i].ammo - } - end - - elements[#elements+1] = {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory_label')} - - for i=1, #data.inventory do - if data.inventory[i].count > 0 then - elements[#elements+1] = { - icon = "fas fa-box", - title = TranslateCap('confiscate_inv', data.inventory[i].count, data.inventory[i].label), - value = data.inventory[i].name, - itemType = 'item_standard', - amount = data.inventory[i].count - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local dataEl = {current = element} - if dataEl.current.value then - TriggerServerEvent('esx_policejob:confiscatePlayerItem', GetPlayerServerId(player), dataEl.current.itemType, dataEl.current.value, dataEl.current.amount) - OpenBodySearchMenu(player) - end - end) - end, GetPlayerServerId(player)) -end - -function OpenFineMenu(player) - if Config.EnableFinePresets then - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('fine')}, - {icon = "fas fa-scroll", title = TranslateCap('traffic_offense'), value = 0}, - {icon = "fas fa-scroll", title = TranslateCap('minor_offense'), value = 1}, - {icon = "fas fa-scroll", title = TranslateCap('average_offense'), value = 2}, - {icon = "fas fa-scroll", title = TranslateCap('major_offense'), value = 3} - } - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - OpenFineCategoryMenu(player, data.current.value) - end) - else - ESX.CloseContext() - OpenFineTextInput(player) - end -end - -local fineList = {} -function OpenFineCategoryMenu(player, category) - if not fineList[category] then - local p = promise.new() - ESX.TriggerServerCallback('esx_policejob:getFineList', function(fines) - p:resolve(fines) - end, category) - fineList[category] = Citizen.Await(p) - end - - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('fine')} - } - - for _,fine in ipairs(fineList[category]) do - elements[#elements+1] = { - icon = "fas fa-scroll", - title = ('%s %s'):format(fine.label, TranslateCap('armory_item', ESX.Math.GroupDigits(fine.amount))), - value = fine.id, - amount = fine.amount, - fineLabel = fine.label - } - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if Config.EnablePlayerManagement then - TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) - else - TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), '', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) - end - - ESX.SetTimeout(300, function() - OpenFineCategoryMenu(player, category) - end) - end) -end - -function OpenFineTextInput(player) - Citizen.CreateThread(function() - local amount = 0 - local reason = '' - AddTextEntry('FMMC_KEY_TIP1', TranslateCap('fine_enter_amount')) - Citizen.Wait(0) - DisplayOnscreenKeyboard(1, 'FMMC_KEY_TIP1', '', '', '', '', '', 30) - while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do - Citizen.Wait(0) - end - if UpdateOnscreenKeyboard() ~= 2 then - amount = tonumber(GetOnscreenKeyboardResult()) - if amount == nil or amount <= 0 then - ESX.ShowNotification(TranslateCap('invalid_amount')) - return - end - end - AddTextEntry('FMMC_KEY_TIP1', TranslateCap('fine_enter_text')) - Citizen.Wait(0) - DisplayOnscreenKeyboard(1, 'FMMC_KEY_TIP1', '', '', '', '', '', 120) - while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do - Citizen.Wait(0) - end - if UpdateOnscreenKeyboard() ~= 2 then - reason = GetOnscreenKeyboardResult() - end - Citizen.Wait(500) - TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', reason, amount) - OpenPoliceActionsMenu() - end) -end - -function LookupVehicle(elementF) - local elements = { - {unselectable = true, icon = "fas fa-car", title = elementF.title}, - {title = TranslateCap('search_plate'), input = true, inputType = "text", inputPlaceholder = "ABC 123"}, - {icon = "fas fa-check-double", title = TranslateCap('lookup_plate'), value = "lookup"} - } - - ESX.OpenContext("right", elements, function(menu,element) - local data = {value = menu.eles[2].inputValue} - local length = data.value and string.len(data.value) or 0 - if not data.value or length < 2 or length > 8 then - ESX.ShowNotification(TranslateCap('search_database_error_invalid')) - else - ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) - local el = { - {unselectable = true, icon = "fas fa-car", title = elementF.title}, - {unselectable = true, icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} - } - if not retrivedInfo.owner then - el[#el+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} - else - el[#el+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} - end - ESX.OpenContext("right", el, nil, function() - OpenPoliceActionsMenu() - end) - end, data.value) - end - end) -end - -function ShowPlayerLicense(player) - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('license_revoke')} - } - - ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(playerData) - if playerData.licenses then - for i=1, #playerData.licenses do - local lic = playerData.licenses[i] - if lic.label and lic.type then - elements[#elements+1] = { - icon = "fas fa-scroll", - title = lic.label, - type = lic.type - } - end - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - ESX.ShowNotification(TranslateCap('licence_you_revoked', data.current.title, playerData.name)) - TriggerServerEvent('esx_policejob:message', GetPlayerServerId(player), TranslateCap('license_revoked', data.current.title)) - TriggerServerEvent('esx_license:removeLicense', GetPlayerServerId(player), data.current.type) - ESX.SetTimeout(300, function() - ShowPlayerLicense(player) - end) - end) - end, GetPlayerServerId(player)) -end - -function OpenUnpaidBillsMenu(player) - local elements = { - {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('unpaid_bills')} - } - - ESX.TriggerServerCallback('esx_billing:getTargetBills', function(bills) - for _,bill in ipairs(bills) do - elements[#elements+1] = { - unselectable = true, - icon = "fas fa-scroll", - title = ('%s - %s'):format(bill.label, TranslateCap('armory_item', ESX.Math.GroupDigits(bill.amount))), - billId = bill.id - } - end - ESX.OpenContext("right", elements, nil, nil) - end, GetPlayerServerId(player)) -end - -function OpenVehicleInfosMenu(vehicleData) - ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo) - local elements = { - {unselectable = true, icon = "fas fa-car", title = TranslateCap('vehicle_info')}, - {icon = "fas fa-car", title = TranslateCap('plate', retrivedInfo.plate)} - } - if not retrivedInfo.owner then - elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner_unknown')} - else - elements[#elements+1] = {unselectable = true, icon = "fas fa-user", title = TranslateCap('owner', retrivedInfo.owner)} - end - ESX.OpenContext("right", elements, nil, nil) - end, vehicleData.plate) -end - -function OpenGetWeaponMenu() - ESX.TriggerServerCallback('esx_policejob:getArmoryWeapons', function(weapons) - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('get_weapon_menu')} - } - for i=1, #weapons do - if weapons[i].count > 0 then - elements[#elements+1] = { - icon = "fas fa-gun", - title = 'x' .. weapons[i].count .. ' ' .. ESX.GetWeaponLabel(weapons[i].name), - value = weapons[i].name - } - end - end - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - ESX.TriggerServerCallback('esx_policejob:removeArmoryWeapon', function() - ESX.CloseContext() - OpenGetWeaponMenu() - end, data.current.value) - end) - end) -end - -function OpenPutWeaponMenu() - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('put_weapon_menu')} - } - local playerPed = PlayerPedId() - local weaponList = ESX.GetWeaponList() - - for i=1, #weaponList do - local weaponHash = joaat(weaponList[i].name) - if HasPedGotWeapon(playerPed, weaponHash, false) and weaponList[i].name ~= 'WEAPON_UNARMED' then - elements[#elements+1] = { - icon = "fas fa-gun", - title = weaponList[i].label, - value = weaponList[i].name - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - ESX.TriggerServerCallback('esx_policejob:addArmoryWeapon', function() - ESX.CloseContext() - OpenPutWeaponMenu() - end, data.current.value, true) - end) -end - -function OpenBuyWeaponsMenu() - local elements = { - {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory_weapontitle')} - } - local playerPed = PlayerPedId() - - for _,v in ipairs(Config.AuthorizedWeapons[ESX.PlayerData.job.grade_name]) do - local weaponNum, weapon = ESX.GetWeapon(v.weapon) - local components, label = {}, '' - local hasWeapon = HasPedGotWeapon(playerPed, joaat(v.weapon), false) - - if v.components then - for i=1, #v.components do - if v.components[i] then - local component = weapon.components[i] - local hasComponent = HasPedGotWeaponComponent(playerPed, joaat(v.weapon), component.hash) - if hasComponent then - label = ('%s: %s'):format(component.label, TranslateCap('armory_owned')) - else - if v.components[i] > 0 then - label = ('%s: %s'):format(component.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.components[i]))) - else - label = ('%s: %s'):format(component.label, TranslateCap('armory_free')) - end - end - components[#components+1] = { - icon = "fas fa-gun", - title = label, - componentLabel = component.label, - hash = component.hash, - name = component.name, - price = v.components[i], - hasComponent = hasComponent, - componentNum = i - } - end - end - end - - if hasWeapon and v.components then - label = ('%s: '):format(weapon.label) - elseif hasWeapon and not v.components then - label = ('%s: %s'):format(weapon.label, TranslateCap('armory_owned')) - else - if v.price > 0 then - label = ('%s: %s'):format(weapon.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.price))) - else - label = ('%s: %s'):format(weapon.label, TranslateCap('armory_free')) - end - end - - elements[#elements+1] = { - icon = "fas fa-gun", - title = label, - weaponLabel = weapon.label, - name = weapon.name, - components = components, - price = v.price, - hasWeapon = hasWeapon - } - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - if data.current.hasWeapon then - if #data.current.components > 0 then - OpenWeaponComponentShop(data.current.components, data.current.name, menu) - end - else - ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) - if bought then - if data.current.price > 0 then - ESX.ShowNotification(TranslateCap('armory_bought', data.current.weaponLabel, ESX.Math.GroupDigits(data.current.price))) - end - ESX.CloseContext() - OpenBuyWeaponsMenu() - else - ESX.ShowNotification(TranslateCap('armory_money')) - end - end, data.current.name, 1) - end - end) -end - -function OpenWeaponComponentShop(components, weaponName, parentShop) - ESX.OpenContext("right", components, function(menu,element) - local data = {current = element} - if data.current.hasComponent then - ESX.ShowNotification(TranslateCap('armory_hascomponent')) - else - ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) - if bought then - if data.current.price > 0 then - ESX.ShowNotification(TranslateCap('armory_bought', data.current.componentLabel, ESX.Math.GroupDigits(data.current.price))) - end - ESX.CloseContext() - parentShop.close() - OpenBuyWeaponsMenu() - else - ESX.ShowNotification(TranslateCap('armory_money')) - end - end, weaponName, 2, data.current.componentNum) - end - end) -end - -function OpenGetStocksMenu() - ESX.TriggerServerCallback('esx_policejob:getStockItems', function(items) - local elements = { - {unselectable = true, icon = "fas fa-box", title = TranslateCap('police_stock')} - } - for i=1, #items do - elements[#elements+1] = { - icon = "fas fa-box", - title = 'x' .. items[i].count .. ' ' .. items[i].label, - value = items[i].name - } - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - local itemName = data.current.value - - local elements2 = { - {unselectable = true, icon = "fas fa-box", title = element.title}, - {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, - {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} - } - - ESX.OpenContext("right", elements2, function(menu2,element2) - local data2 = {value = menu2.eles[2].inputValue} - local count = tonumber(data2.value) - if not count then - ESX.ShowNotification(TranslateCap('quantity_invalid')) - else - ESX.CloseContext() - TriggerServerEvent('esx_policejob:getStockItem', itemName, count) - Wait(300) - OpenGetStocksMenu() - end - end) - end) - end) -end - -function OpenPutStocksMenu() - ESX.TriggerServerCallback('esx_policejob:getPlayerInventory', function(inventory) - local elements = { - {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory')} - } - for i=1, #inventory.items do - local item = inventory.items[i] - if item.count > 0 then - elements[#elements+1] = { - icon = "fas fa-box", - title = item.label .. ' x' .. item.count, - type = 'item_standard', - value = item.name - } - end - end - - ESX.OpenContext("right", elements, function(menu,element) - local data = {current = element} - local itemName = data.current.value - - local elements2 = { - {unselectable = true, icon = "fas fa-box", title = element.title}, - {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, - {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} - } - - ESX.OpenContext("right", elements2, function(menu2,element2) - local data2 = {value = menu2.eles[2].inputValue} - local count = tonumber(data2.value) - if not count then - ESX.ShowNotification(TranslateCap('quantity_invalid')) - else - ESX.CloseContext() - TriggerServerEvent('esx_policejob:putStockItems', itemName, count) - Wait(300) - OpenPutStocksMenu() - end - end) - end) - end) -end - -RegisterNetEvent('esx_phone:loaded') -AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) - local specialContact = { - name = TranslateCap('phone_police'), - number = 'police', - base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDFGQTJDRkI0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDFGQTJDRkM0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0MUZBMkNGOTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0MUZBMkNGQTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoW66EYAAAjGSURBVHjapJcLcFTVGcd/u3cfSXaTLEk2j80TCI8ECI9ABCyoiBqhBVQqVG2ppVKBQqUVgUl5OU7HKqNOHUHU0oHamZZWoGkVS6cWAR2JPJuAQBPy2ISEvLN57+v2u2E33e4k6Ngz85+9d++95/zP9/h/39GpqsqiRYsIGz8QZAq28/8PRfC+4HT4fMXFxeiH+GC54NeCbYLLATLpYe/ECx4VnBTsF0wWhM6lXY8VbBE0Ch4IzLcpfDFD2P1TgrdC7nMCZLRxQ9AkiAkQCn77DcH3BC2COoFRkCSIG2JzLwqiQi0RSmCD4JXbmNKh0+kc/X19tLtc9Ll9sk9ZS1yoU71YIk3xsbEx8QaDEc2ttxmaJSKC1ggSKBK8MKwTFQVXRzs3WzpJGjmZgvxcMpMtWIwqsjztvSrlzjYul56jp+46qSmJmMwR+P3+4aZ8TtCprRkk0DvUW7JjmV6lsqoKW/pU1q9YQOE4Nxkx4ladE7zd8ivuVmJQfXZKW5dx5EwPRw4fxNx2g5SUVLw+33AkzoRaQDP9SkFu6OKqz0uF8yaz7vsOL6ycQVLkcSg/BlWNsjuFoKE1knqDSl5aNnmPLmThrE0UvXqQqvJPyMrMGorEHwQfEha57/3P7mXS684GFjy8kreLppPUuBXfyd/ibeoS2kb0mWPANhJdYjb61AxUvx5PdT3+4y+Tb3mTd19ZSebE+VTXVGNQlHAC7w4VhH8TbA36vKq6ilnzlvPSunHw6Trc7XpZ14AyfgYeyz18crGN1Alz6e3qwNNQSv4dZox1h/BW9+O7eIaEsVv41Y4XeHJDG83Nl4mLTwzGhJYtx0PzNTjOB9KMTlc7Nkcem39YAGU7cbeBKVLMPGMVf296nMd2VbBq1wmizHoqqm/wrS1/Zf0+N19YN2PIu1fcIda4Vk66Zx/rVi+jo9eIX9wZGGcFXUMR6BHUa76/2ezioYcXMtpyAl91DSaTfDxlJbtLprHm2ecpObqPuTPzSNV9yKz4a4zJSuLo71/j8Q17ON69EmXiPIlNMe6FoyzOqWPW/MU03Lw5EFcyKghTrNDh7+/vw545mcJcWbTiGKpRdGPMXbx90sGmDaux6sXk+kimjU+BjnMkx3kYP34cXrFuZ+3nrHi6iDMt92JITcPjk3R3naRwZhpuNSqoD93DKaFVU7j2dhcF8+YzNlpErbIBTVh8toVccbaysPB+4pMcuPw25kwSsau7BIlmHpy3guaOPtISYyi/UkaJM5Lpc5agq5Xkcl6gIHkmqaMn0dtylcjIyPThCNyhaXyfR2W0I1our0v6qBii07ih5rDtGSOxNVdk1y4R2SR8jR/g7hQD9l1jUeY/WLJB5m39AlZN4GZyIQ1fFJNsEgt0duBIc5GRkcZF53mNwIzhXPDgQPoZIkiMkbTxtstDMVnmFA4cOsbz2/aKjSQjev4Mp9ZAg+hIpFhB3EH5Yal16+X+Kq3dGfxkzRY+KauBjBzREvGN0kNCTARu94AejBLMHorAQ7cEQMGs2cXvkWshYLDi6e9l728O8P1XW6hKeB2yv42q18tjj+iFTGoSi+X9jJM9RTxS9E+OHT0krhNiZqlbqraoT7RAU5bBGrEknEBhgJks7KXbLS8qERI0ErVqF/Y4K6NHZfLZB+/wzJvncacvFd91oXO3o/O40MfZKJOKu/rne+mRQByXM4lYreb1tUnkizVVA/0SpfpbWaCNBeEE5gb/UH19NLqEgDF+oNDQWcn41Cj0EXFEWqzkOIyYekslFkThsvMxpIyE2hIc6lXGZ6cPyK7Nnk5OipixRdxgUESAYmhq68VsGgy5CYKCUAJTg0+izApXne3CJFmUTwg4L3FProFxU+6krqmXu3MskkhSD2av41jLdzlnfFrSdCZxyqfMnppN6ZUa7pwt0h3fiK9DCt4IO9e7YqisvI7VYgmNv7mhBKKD/9psNi5dOMv5ZjukjsLdr0ffWsyTi6eSlfcA+dmiVyOXs+/sHNZu3M6PdxzgVO9GmDSHsSNqmTz/R6y6Xxqma4fwaS5Mn85n1ZE0Vl3CHBER3lUNEhiURpPJRFdTOcVnpUJnPIhR7cZXfoH5UYc5+E4RzRH3sfSnl9m2dSMjE+Tz9msse+o5dr7UwcQ5T3HwlWUkNuzG3dKFSTbsNs7m/Y8vExOlC29UWkMJlAxKoRQMR3IC7x85zOn6fHS50+U/2Untx2R1voinu5no+DQmz7yPXmMKZnsu0wrm0Oe3YhOVHdm8A09dBQYhTv4T7C+xUPrZh8Qn2MMr4qcDSRfoirWgKAvtgOpv1JI8Zi77X15G7L+fxeOUOiUFxZiULD5fSlNzNM62W+k1yq5gjajGX/ZHvOIyxd+Fkj+P092rWP/si0Qr7VisMaEWuCiYonXFwbAUTWWPYLV245NITnGkUXnpI9butLJn2y6iba+hlp7C09qBcvoN7FYL9mhxo1/y/LoEXK8Pv6qIC8WbBY/xr9YlPLf9dZT+OqKTUwfmDBm/GOw7ws4FWpuUP2gJEZvKqmocuXPZuWYJMzKuSsH+SNwh3bo0p6hao6HeEqwYEZ2M6aKWd3PwTCy7du/D0F1DsmzE6/WGLr5LsDF4LggnYBacCOboQLHQ3FFfR58SR+HCR1iQH8ukhA5s5o5AYZMwUqOp74nl8xvRHDlRTsnxYpJsUjtsceHt2C8Fm0MPJrphTkZvBc4It9RKLOFx91Pf0Igu0k7W2MmkOewS2QYJUJVWVz9VNbXUVVwkyuAmKTFJayrDo/4Jwe/CT0aGYTrWVYEeUfsgXssMRcpyenraQJa0VX9O3ZU+Ma1fax4xGxUsUVFkOUbcama1hf+7+LmA9juHWshwmwOE1iMmCFYEzg1jtIm1BaxW6wCGGoFdewPfvyE4ertTiv4rHC73B855dwp2a23bbd4tC1hvhOCbX7b4VyUQKhxrtSOaYKngasizvwi0RmOS4O1QZf2yYfiaR+73AvhTQEVf+rpn9/8IMAChKDrDzfsdIQAAAABJRU5ErkJggg==' - } - TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) -end) - -AddEventHandler('esx_phone:cancelMessage', function(dispatchNumber) - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and dispatchNumber == 'police' then - if Config.EnableESXService and not playerInService then - CancelEvent() - end - end -end) - -AddEventHandler('esx_policejob:hasEnteredMarker', function(station, part, partNum) - if part == 'Cloakroom' then - CurrentAction = 'menu_cloakroom' - CurrentActionMsg = TranslateCap('open_cloackroom') - CurrentActionData = {} - elseif part == 'Armory' then - CurrentAction = 'menu_armory' - CurrentActionMsg = TranslateCap('open_armory') - CurrentActionData = {station = station} - elseif part == 'Vehicles' then - CurrentAction = 'menu_vehicle_spawner' - CurrentActionMsg = TranslateCap('garage_prompt') - CurrentActionData = {station = station, part = part, partNum = partNum} - elseif part == 'Helicopters' then - CurrentAction = 'Helicopters' - CurrentActionMsg = TranslateCap('helicopter_prompt') - CurrentActionData = {station = station, part = part, partNum = partNum} - elseif part == 'BossActions' then - CurrentAction = 'menu_boss_actions' - CurrentActionMsg = TranslateCap('open_bossmenu') - CurrentActionData = {} - end -end) - -AddEventHandler('esx_policejob:hasExitedMarker', function() - if not isInShopMenu then - ESX.CloseContext() - end - CurrentAction = nil -end) - -AddEventHandler('esx_policejob:hasEnteredEntityZone', function(entity) - local playerPed = PlayerPedId() - - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and IsPedOnFoot(playerPed) then - CurrentAction = 'remove_entity' - CurrentActionMsg = TranslateCap('remove_prop') - CurrentActionData = {entity = entity} - end - - if GetEntityModel(entity) == `p_ld_stinger_s` then - if IsPedInAnyVehicle(playerPed, false) then - local vehicle = GetVehiclePedIsIn(playerPed) - for i=0, 7 do - SetVehicleTyreBurst(vehicle, i, true, 1000) - end - end - end -end) - -AddEventHandler('esx_policejob:hasExitedEntityZone', function(entity) - if CurrentAction == 'remove_entity' and CurrentActionData and CurrentActionData.entity == entity then - CurrentAction = nil - end -end) - -RegisterNetEvent('esx_policejob:handcuff') -AddEventHandler('esx_policejob:handcuff', function() - isHandcuffed = not isHandcuffed - local playerPed = PlayerPedId() - - if isHandcuffed then - playCuffAnim(playerPed) - - SetEnableHandcuffs(playerPed, true) - DisablePlayerFiring(playerPed, true) - SetCurrentPedWeapon(playerPed, `WEAPON_UNARMED`, true) - SetPedCanPlayGestureAnims(playerPed, false) - - if Config.Cuffs and Config.Cuffs.FreezePlayer then - FreezeEntityPosition(playerPed, true) - else - FreezeEntityPosition(playerPed, false) - SetPedCanRagdoll(playerPed, true) - SetPedMoveRateOverride(playerPed, 1.0) - SetRunSprintMultiplierForPlayer(PlayerId(), 1.0) - end - - DisplayRadar(false) - SetEntityCollision(playerPed, true, true) - SetEntityDynamic(playerPed, true) - TriggerServerEvent('esx_policejob:cuffsState', true) - - if Config.EnableHandcuffTimer then - if handcuffTimer.active then ESX.ClearTimeout(handcuffTimer.task) end - StartHandcuffTimer() - end - else - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - - stopCuffAnim(playerPed) - SetEnableHandcuffs(playerPed, false) - DisablePlayerFiring(playerPed, false) - SetPedCanPlayGestureAnims(playerPed, true) - FreezeEntityPosition(playerPed, false) - DisplayRadar(true) - SetEntityCollision(playerPed, true, true) - SetEntityDynamic(playerPed, true) - - TriggerServerEvent('esx_policejob:cuffsState', false) - end -end) - -RegisterNetEvent('esx_policejob:unrestrain') -AddEventHandler('esx_policejob:unrestrain', function() - if not isHandcuffed then return end - local playerPed = PlayerPedId() - isHandcuffed = false - - stopCuffAnim(playerPed) - SetEnableHandcuffs(playerPed, false) - DisablePlayerFiring(playerPed, false) - SetPedCanPlayGestureAnims(playerPed, true) - FreezeEntityPosition(playerPed, false) - DisplayRadar(true) - - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - - TriggerServerEvent('esx_policejob:cuffsState', false) -end) - -CreateThread(function() - while true do - if isHandcuffed and (not Config.Cuffs or not Config.Cuffs.FreezePlayer) then - local ped = PlayerPedId() - - if IsPedUsingAnyScenario(ped) then - ClearPedTasksImmediately(ped) - end - - if IsEntityPlayingAnim(ped, ARREST_DICT, ARREST_ANIM, 3) ~= 1 then - playCuffAnim(ped) - end - - FreezeEntityPosition(ped, false) - - DisableControlAction(0, 24, true) -- Attack - DisableControlAction(0, 25, true) -- Aim - DisableControlAction(0, 37, true) -- Select Weapon - DisableControlAction(0, 140, true) -- Melee light - DisableControlAction(0, 141, true) -- Melee heavy - DisableControlAction(0, 142, true) -- Melee alt - DisableControlAction(0, 47, true) -- Weapon - DisableControlAction(2, 36, true) -- Stealth - Wait(0) - else - Wait(300) - end - end -end) - -RegisterNetEvent('esx_policejob:drag') -AddEventHandler('esx_policejob:drag', function(copId) - if isHandcuffed then - dragStatus.isDragged = not dragStatus.isDragged - dragStatus.CopId = copId - end -end) - -CreateThread(function() - local wasDragged - while true do - local sleep = 1500 - if isHandcuffed and dragStatus.isDragged then - sleep = 50 - local targetPed = GetPlayerPed(GetPlayerFromServerId(dragStatus.CopId)) - if DoesEntityExist(targetPed) and IsPedOnFoot(targetPed) and not Player(dragStatus.CopId).state.isDead then - if not wasDragged then - AttachEntityToEntity(PlayerPedId(), targetPed, 11816, 0.54, 0.54, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) - wasDragged = true - else - Wait(1000) - end - else - wasDragged = false - dragStatus.isDragged = false - DetachEntity(PlayerPedId(), true, false) - end - elseif wasDragged then - wasDragged = false - DetachEntity(PlayerPedId(), true, false) - end - Wait(sleep) - end -end) - -RegisterNetEvent('esx_policejob:putInVehicle') -AddEventHandler('esx_policejob:putInVehicle', function() - if isHandcuffed then - local playerPed = PlayerPedId() - local vehicle, distance = ESX.Game.GetClosestVehicle() - if vehicle and distance < 5.0 then - local maxSeats, freeSeat = GetVehicleMaxNumberOfPassengers(vehicle) - for i=maxSeats - 1, 0, -1 do - if IsVehicleSeatFree(vehicle, i) then freeSeat = i break end - end - if freeSeat then - TaskWarpPedIntoVehicle(playerPed, vehicle, freeSeat) - dragStatus.isDragged = false - end - end - end -end) - -RegisterNetEvent('esx_policejob:OutVehicle') -AddEventHandler('esx_policejob:OutVehicle', function() - local ped = PlayerPedId() - if IsPedSittingInAnyVehicle(ped) then - local vehicle = GetVehiclePedIsIn(ped, false) - TaskLeaveVehicle(ped, vehicle, 64) - end -end) - -CreateThread(function() - while true do - local sleep = 1000 - if isHandcuffed and Config.Cuffs and Config.Cuffs.FreezePlayer then - sleep = 0 - DisableControlAction(0, 1, true) -- Look L/R - DisableControlAction(0, 2, true) -- Look U/D - DisableControlAction(0, 24, true) -- Attack - DisableControlAction(0, 257, true) -- Attack2 - DisableControlAction(0, 25, true) -- Aim - DisableControlAction(0, 263, true) -- Melee - DisableControlAction(0, 32, true) -- W - DisableControlAction(0, 34, true) -- A - DisableControlAction(0, 31, true) -- S - DisableControlAction(0, 30, true) -- D - DisableControlAction(0, 45, true) -- Reload - DisableControlAction(0, 22, true) -- Jump - DisableControlAction(0, 44, true) -- Cover - DisableControlAction(0, 37, true) -- Select Weapon - DisableControlAction(0, 23, true) -- Enter - DisableControlAction(0, 288, true) -- Phone - DisableControlAction(0, 289, true) -- Inventory - DisableControlAction(0, 170, true) -- Animations - DisableControlAction(0, 167, true) -- Job - DisableControlAction(0, 0, true) -- View - DisableControlAction(0, 26, true) -- Look behind - DisableControlAction(0, 73, true) -- Clear anim - DisableControlAction(2, 199, true) -- Pause - DisableControlAction(0, 59, true) -- Steer in vehicle - DisableControlAction(0, 71, true) -- Forward in vehicle - DisableControlAction(0, 72, true) -- Reverse in vehicle - DisableControlAction(2, 36, true) -- Stealth - DisableControlAction(0, 47, true) -- Weapon - DisableControlAction(0, 264, true) - DisableControlAction(0, 257, true) - DisableControlAction(0, 140, true) - DisableControlAction(0, 141, true) - DisableControlAction(0, 142, true) - DisableControlAction(0, 143, true) - DisableControlAction(0, 75, true) -- Exit vehicle - DisableControlAction(27, 75, true) - -local ped = PlayerPedId() - if IsEntityPlayingAnim(ped, 'mp_arresting', 'idle', 3) ~= 1 then - ESX.Streaming.RequestAnimDict('mp_arresting', function() - TaskPlayAnim(ped, 'mp_arresting', 'idle', 8.0, -8.0, -1, 49, 0.0, false, false, false) - RemoveAnimDict('mp_arresting') - end) - end - end - Wait(sleep) - end -end) - -CreateThread(function() - for _,v in pairs(Config.PoliceStations) do - local blip = AddBlipForCoord(v.Blip.Coords) - SetBlipSprite (blip, v.Blip.Sprite) - SetBlipDisplay(blip, v.Blip.Display) - SetBlipScale (blip, v.Blip.Scale) - SetBlipColour (blip, v.Blip.Colour) - SetBlipAsShortRange(blip, true) - BeginTextCommandSetBlipName('STRING') - AddTextComponentSubstringPlayerName(TranslateCap('map_blip')) - EndTextCommandSetBlipName(blip) - end -end) - -CreateThread(function() - while true do - local Sleep = 1500 - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then - Sleep = 500 - local playerPed = PlayerPedId() - local playerCoords = GetEntityCoords(playerPed) - local isInMarker, hasExited = false, false - local currentStation, currentPart, currentPartNum - - for k,v in pairs(Config.PoliceStations) do - for i=1, #v.Cloakrooms do - local distance = #(playerCoords - v.Cloakrooms[i]) - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Cloakrooms, v.Cloakrooms[i], 0.0, 0.0, 0.0, 0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Cloakroom', i - end - end - end - - for i=1, #v.Armories do - local distance = #(playerCoords - v.Armories[i]) - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Armories, v.Armories[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Armory', i - end - end - end - - for i=1, #v.Vehicles do - local distance = #(playerCoords - v.Vehicles[i].Spawner) - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Vehicles, v.Vehicles[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Vehicles', i - end - end - end - - for i=1, #v.Helicopters do - local distance = #(playerCoords - v.Helicopters[i].Spawner) - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.Helicopters, v.Helicopters[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Helicopters', i - end - end - end - - if Config.EnablePlayerManagement and ESX.PlayerData.job.grade_name == 'boss' then - for i=1, #v.BossActions do - local distance = #(playerCoords - v.BossActions[i]) - if distance < Config.DrawDistance then - DrawMarker(Config.MarkerType.BossActions, v.BossActions[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) - Sleep = 0 - if distance < Config.MarkerSize.x then - isInMarker, currentStation, currentPart, currentPartNum = true, k, 'BossActions', i - end - end - end - end - end - - if (isInMarker and not HasAlreadyEnteredMarker) or (isInMarker and (LastStation ~= currentStation or LastPart ~= currentPart or LastPartNum ~= currentPartNum)) then - if (LastStation and LastPart and LastPartNum) and (LastStation ~= currentStation or LastPart ~= currentPart or LastPartNum ~= currentPartNum) then - TriggerEvent('esx_policejob:hasExitedMarker', LastStation, LastPart, LastPartNum) - hasExited = true - end - HasAlreadyEnteredMarker = true - LastStation = currentStation - LastPart = currentPart - LastPartNum = currentPartNum - TriggerEvent('esx_policejob:hasEnteredMarker', currentStation, currentPart, currentPartNum) - end - - if not hasExited and not isInMarker and HasAlreadyEnteredMarker then - HasAlreadyEnteredMarker = false - TriggerEvent('esx_policejob:hasExitedMarker', LastStation, LastPart, LastPartNum) - end - end - Wait(Sleep) - end -end) - -CreateThread(function() - local trackedEntities = { - `prop_roadcone02a`, - `prop_barrier_work05`, - `p_ld_stinger_s`, - `prop_boxpile_07d`, - `hei_prop_cash_crate_half_full` - } - - while true do - local Sleep = 1500 - if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then - local playerCoords = GetEntityCoords(PlayerPedId()) - local closestDistance = -1 - local closestEntity = nil - - for i=1, #trackedEntities do - local object = GetClosestObjectOfType(playerCoords, 3.0, trackedEntities[i], false, false, false) - if DoesEntityExist(object) then - Sleep = 500 - local objCoords = GetEntityCoords(object) - local distance = #(playerCoords - objCoords) - if closestDistance == -1 or closestDistance > distance then - closestDistance = distance - closestEntity = object - end - end - end - - if closestDistance ~= -1 and closestDistance <= 3.0 then - if LastEntity ~= closestEntity then - TriggerEvent('esx_policejob:hasEnteredEntityZone', closestEntity) - LastEntity = closestEntity - end - else - if LastEntity then - TriggerEvent('esx_policejob:hasExitedEntityZone', LastEntity) - LastEntity = nil - end - end - end - Wait(Sleep) - end -end) - -ESX.RegisterInput("police:interact", "(ESX PoliceJob) " .. TranslateCap('interaction'), "keyboard", "E", function() - if not CurrentAction then return end - if (not ESX.PlayerData.job) or (ESX.PlayerData.job.name ~= 'police') then - return - end - - if CurrentAction == 'menu_cloakroom' then - OpenCloakroomMenu() - elseif CurrentAction == 'menu_armory' then - if not Config.EnableESXService or playerInService then - OpenArmoryMenu(CurrentActionData.station) - else - ESX.ShowNotification(TranslateCap('service_not')) - end - elseif CurrentAction == 'menu_vehicle_spawner' then - if not Config.EnableESXService or playerInService then - OpenVehicleSpawnerMenu('car', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) - else - ESX.ShowNotification(TranslateCap('service_not')) - end - elseif CurrentAction == 'Helicopters' then - if not Config.EnableESXService or playerInService then - OpenVehicleSpawnerMenu('helicopter', CurrentActionData.station, CurrentActionData.part, CurrentActionData.partNum) - else - ESX.ShowNotification(TranslateCap('service_not')) - end - elseif CurrentAction == 'delete_vehicle' then - ESX.Game.DeleteVehicle(CurrentActionData.vehicle) - elseif CurrentAction == 'menu_boss_actions' then - ESX.CloseContext() - TriggerEvent('esx_society:openBossMenu', 'police', function() - ESX.CloseContext() - CurrentAction = 'menu_boss_actions' - CurrentActionMsg = TranslateCap('open_bossmenu') - CurrentActionData = {} - end, { wash = false }) - elseif CurrentAction == 'remove_entity' then - if CurrentActionData and CurrentActionData.entity and DoesEntityExist(CurrentActionData.entity) then - DeleteEntity(CurrentActionData.entity) - end - end - - CurrentAction = nil -end) - -ESX.RegisterInput("police:quickactions", "(ESX PoliceJob) "..TranslateCap('quick_actions'), "keyboard", "F6", function() - if not ESX.PlayerData.job or (ESX.PlayerData.job.name ~= 'police') or isDead then - return - end - if not Config.EnableESXService or playerInService then - OpenPoliceActionsMenu() - else - ESX.ShowNotification(TranslateCap('service_not')) - end -end) - -CreateThread(function() - while true do - local Sleep = 1000 - if CurrentAction then - Sleep = 0 - ESX.ShowHelpNotification(CurrentActionMsg) - end - Wait(Sleep) - end -end) - -function createBlip(id) - local ped = GetPlayerPed(id) - local blip = GetBlipFromEntity(ped) - if not DoesBlipExist(blip) then - blip = AddBlipForEntity(ped) - SetBlipSprite(blip, 1) - ShowHeadingIndicatorOnBlip(blip, true) - SetBlipRotation(blip, math.ceil(GetEntityHeading(ped))) - SetBlipNameToPlayerName(blip, id) - SetBlipScale(blip, 0.85) - SetBlipAsShortRange(blip, true) - table.insert(blipsCops, blip) - end -end - -AddEventHandler('esx:onPlayerSpawn', function() - isDead = false - TriggerEvent('esx_policejob:unrestrain') - if not hasAlreadyJoined then - TriggerServerEvent('esx_policejob:spawned') - end - hasAlreadyJoined = true -end) - -AddEventHandler('esx:onPlayerDeath', function() - isDead = true -end) - -AddEventHandler('onResourceStop', function(resource) - if resource == GetCurrentResourceName() then - TriggerEvent('esx_policejob:unrestrain') - TriggerEvent('esx_phone:removeSpecialContact', 'police') - - if Config.EnableESXService then - TriggerServerEvent('esx_service:disableService', 'police') - end - - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - end -end) - -function StartHandcuffTimer() - if Config.EnableHandcuffTimer and handcuffTimer.active then - ESX.ClearTimeout(handcuffTimer.task) - end - handcuffTimer.active = true - handcuffTimer.task = ESX.SetTimeout(Config.HandcuffTimer, function() - ESX.ShowNotification(TranslateCap('unrestrained_timer')) - TriggerEvent('esx_policejob:unrestrain') - handcuffTimer.active = false - end) -end - -function ImpoundVehicle(vehicle) - ESX.Game.DeleteVehicle(vehicle) - ESX.ShowNotification(TranslateCap('impound_successful')) - currentTask.busy = false -end diff --git a/[esx_addons]/esx_policejob/client/markers.lua b/[esx_addons]/esx_policejob/client/markers.lua new file mode 100644 index 00000000..b4a2c019 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/markers.lua @@ -0,0 +1,209 @@ +local A = POLICE + +AddEventHandler('esx_policejob:hasEnteredMarker', function(station, part, partNum) + if part == 'Cloakroom' then + A.CurrentAction = 'menu_cloakroom' + A.CurrentActionMsg = TranslateCap('open_cloackroom') + A.CurrentActionData = {} + + elseif part == 'Armory' then + A.CurrentAction = 'menu_armory' + A.CurrentActionMsg = TranslateCap('open_armory') + A.CurrentActionData = { station = station } + + elseif part == 'Vehicles' then + A.CurrentAction = 'menu_vehicle_spawner' + A.CurrentActionMsg = TranslateCap('garage_prompt') + A.CurrentActionData = { station = station, part = part, partNum = partNum } + + elseif part == 'Helicopters' then + A.CurrentAction = 'Helicopters' + A.CurrentActionMsg = TranslateCap('helicopter_prompt') + A.CurrentActionData = { station = station, part = part, partNum = partNum } + + elseif part == 'BossActions' then + A.CurrentAction = 'menu_boss_actions' + A.CurrentActionMsg = TranslateCap('open_bossmenu') + A.CurrentActionData = {} + end +end) + +AddEventHandler('esx_policejob:hasExitedMarker', function() + if not A.isInShopMenu then + ESX.CloseContext() + end + A.CurrentAction = nil +end) + +AddEventHandler('esx_policejob:hasEnteredEntityZone', function(entity) + local playerPed = PlayerPedId() + + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and IsPedOnFoot(playerPed) then + A.CurrentAction = 'remove_entity' + A.CurrentActionMsg = TranslateCap('remove_prop') + A.CurrentActionData = { entity = entity } + end + + if GetEntityModel(entity) == `p_ld_stinger_s` then + if IsPedInAnyVehicle(playerPed, false) then + local vehicle = GetVehiclePedIsIn(playerPed) + for i = 0, 7 do + SetVehicleTyreBurst(vehicle, i, true, 1000) + end + end + end +end) + +AddEventHandler('esx_policejob:hasExitedEntityZone', function(entity) + if A.CurrentAction == 'remove_entity' and A.CurrentActionData and A.CurrentActionData.entity == entity then + A.CurrentAction = nil + end +end) + + +CreateThread(function() + while true do + local Sleep = 1500 + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then + Sleep = 500 + local playerPed = PlayerPedId() + local playerCoords = GetEntityCoords(playerPed) + local isInMarker, hasExited = false, false + local currentStation, currentPart, currentPartNum + + for k, v in pairs(Config.PoliceStations) do + -- Vestiaires + for i=1, #v.Cloakrooms do + local distance = #(playerCoords - v.Cloakrooms[i]) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Cloakrooms, v.Cloakrooms[i], 0.0, 0.0, 0.0, 0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Cloakroom', i + end + end + end + + -- Armureries + for i=1, #v.Armories do + local distance = #(playerCoords - v.Armories[i]) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Armories, v.Armories[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Armory', i + end + end + end + + -- Garage voitures + for i=1, #v.Vehicles do + local distance = #(playerCoords - v.Vehicles[i].Spawner) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Vehicles, v.Vehicles[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Vehicles', i + end + end + end + + -- Héliport + for i=1, #v.Helicopters do + local distance = #(playerCoords - v.Helicopters[i].Spawner) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.Helicopters, v.Helicopters[i].Spawner, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'Helicopters', i + end + end + end + + -- Boss actions + if Config.EnablePlayerManagement and ESX.PlayerData.job.grade_name == 'boss' then + for i=1, #v.BossActions do + local distance = #(playerCoords - v.BossActions[i]) + if distance < Config.DrawDistance then + DrawMarker(Config.MarkerType.BossActions, v.BossActions[i], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, Config.MarkerColor.r, Config.MarkerColor.g, Config.MarkerColor.b, 100, false, true, 2, true, false, false, false) + Sleep = 0 + if distance < Config.MarkerSize.x then + isInMarker, currentStation, currentPart, currentPartNum = true, k, 'BossActions', i + end + end + end + end + end + + -- Enter/Exit + if (isInMarker and not A.HasAlreadyEnteredMarker) or + (isInMarker and (A.LastStation ~= currentStation or A.LastPart ~= currentPart or A.LastPartNum ~= currentPartNum)) then + + if (A.LastStation and A.LastPart and A.LastPartNum) and + (A.LastStation ~= currentStation or A.LastPart ~= currentPart or A.LastPartNum ~= currentPartNum) then + TriggerEvent('esx_policejob:hasExitedMarker', A.LastStation, A.LastPart, A.LastPartNum) + hasExited = true + end + + A.HasAlreadyEnteredMarker = true + A.LastStation = currentStation + A.LastPart = currentPart + A.LastPartNum = currentPartNum + + TriggerEvent('esx_policejob:hasEnteredMarker', currentStation, currentPart, currentPartNum) + end + + if not hasExited and not isInMarker and A.HasAlreadyEnteredMarker then + A.HasAlreadyEnteredMarker = false + TriggerEvent('esx_policejob:hasExitedMarker', A.LastStation, A.LastPart, A.LastPartNum) + end + end + Wait(Sleep) + end +end) + + +CreateThread(function() + local trackedEntities = { + `prop_roadcone02a`, + `prop_barrier_work05`, + `p_ld_stinger_s`, + `prop_boxpile_07d`, + `hei_prop_cash_crate_half_full` + } + + while true do + local Sleep = 1500 + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then + local playerCoords = GetEntityCoords(PlayerPedId()) + local closestDistance = -1 + local closestEntity = nil + + for i=1, #trackedEntities do + local object = GetClosestObjectOfType(playerCoords, 3.0, trackedEntities[i], false, false, false) + if DoesEntityExist(object) then + Sleep = 500 + local objCoords = GetEntityCoords(object) + local distance = #(playerCoords - objCoords) + if closestDistance == -1 or closestDistance > distance then + closestDistance = distance + closestEntity = object + end + end + end + + if closestDistance ~= -1 and closestDistance <= 3.0 then + if A.LastEntity ~= closestEntity then + TriggerEvent('esx_policejob:hasEnteredEntityZone', closestEntity) + A.LastEntity = closestEntity + end + else + if A.LastEntity then + TriggerEvent('esx_policejob:hasExitedEntityZone', A.LastEntity) + A.LastEntity = nil + end + end + end + Wait(Sleep) + end +end) diff --git a/[esx_addons]/esx_policejob/client/menus/actions.lua b/[esx_addons]/esx_policejob/client/menus/actions.lua new file mode 100644 index 00000000..2bbb01fc --- /dev/null +++ b/[esx_addons]/esx_policejob/client/menus/actions.lua @@ -0,0 +1,163 @@ +local A = POLICE + +function OpenPoliceActionsMenu() + local elements = { + {unselectable = true, icon = "fas fa-police", title = TranslateCap('menu_title')}, + {icon = "fas fa-user", title = TranslateCap('citizen_interaction'), value = 'citizen_interaction'}, + {icon = "fas fa-car", title = TranslateCap('vehicle_interaction'), value = 'vehicle_interaction'}, + {icon = "fas fa-object", title = TranslateCap('object_spawner'), value = 'object_spawner'} + } + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + + if data.current.value == 'citizen_interaction' then + local elements2 = { + {unselectable = true, icon = "fas fa-user", title = element.title}, + {icon = "fas fa-idkyet", title = TranslateCap('id_card'), value = 'identity_card'}, + {icon = "fas fa-idkyet", title = TranslateCap('search'), value = 'search'}, + {icon = "fas fa-idkyet", title = TranslateCap('handcuff'), value = 'handcuff'}, + {icon = "fas fa-idkyet", title = TranslateCap('drag'), value = 'drag'}, + {icon = "fas fa-idkyet", title = TranslateCap('put_in_vehicle'), value = 'put_in_vehicle'}, + {icon = "fas fa-idkyet", title = TranslateCap('out_the_vehicle'), value = 'out_the_vehicle'}, + {icon = "fas fa-idkyet", title = TranslateCap('fine'), value = 'fine'}, + {icon = "fas fa-idkyet", title = TranslateCap('unpaid_bills'), value = 'unpaid_bills'} + } + + if Config.EnableLicenses then + elements2[#elements2+1] = { + icon = "fas fa-scroll", + title = TranslateCap('license_check'), + value = 'license' + } + end + + ESX.OpenContext("right", elements2, function(menu2,element2) + local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() + if closestPlayer ~= -1 and closestDistance <= 3.0 then + local data2 = {current = element2} + local action = data2.current.value + + if action == 'identity_card' then + OpenIdentityCardMenu(closestPlayer) + elseif action == 'search' then + OpenBodySearchMenu(closestPlayer) + elseif action == 'handcuff' then + TriggerServerEvent('esx_policejob:handcuff', GetPlayerServerId(closestPlayer)) + elseif action == 'drag' then + TriggerServerEvent('esx_policejob:drag', GetPlayerServerId(closestPlayer)) + elseif action == 'put_in_vehicle' then + TriggerServerEvent('esx_policejob:putInVehicle', GetPlayerServerId(closestPlayer)) + elseif action == 'out_the_vehicle' then + TriggerServerEvent('esx_policejob:OutVehicle', GetPlayerServerId(closestPlayer)) + elseif action == 'fine' then + OpenFineMenu(closestPlayer) + elseif action == 'license' then + ShowPlayerLicense(closestPlayer) + elseif action == 'unpaid_bills' then + OpenUnpaidBillsMenu(closestPlayer) + end + else + ESX.ShowNotification(TranslateCap('no_players_nearby')) + end + end, function(menu) + OpenPoliceActionsMenu() + end) + + elseif data.current.value == 'vehicle_interaction' then + local elements3 = { + {unselectable = true, icon = "fas fa-car", title = element.title} + } + local playerPed = PlayerPedId() + local vehicle = ESX.Game.GetVehicleInDirection() + + if DoesEntityExist(vehicle) then + elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('vehicle_info'), value = 'vehicle_infos'} + elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('pick_lock'), value = 'hijack_vehicle'} + elements3[#elements3+1] = {icon = "fas fa-car", title = TranslateCap('impound'), value = 'impound'} + end + + elements3[#elements3+1] = { + icon = "fas fa-scroll", + title = TranslateCap('search_database'), + value = 'search_database' + } + + ESX.OpenContext("right", elements3, function(menu3,element3) + local data2 = {current = element3} + local coords = GetEntityCoords(playerPed) + vehicle = ESX.Game.GetVehicleInDirection() + local action = data2.current.value + + if action == 'search_database' then + LookupVehicle(element3) + elseif DoesEntityExist(vehicle) then + if action == 'vehicle_infos' then + local vehicleData = ESX.Game.GetVehicleProperties(vehicle) + OpenVehicleInfosMenu(vehicleData) + elseif action == 'hijack_vehicle' then + if IsAnyVehicleNearPoint(coords.x, coords.y, coords.z, 3.0) then + TaskStartScenarioInPlace(playerPed, 'WORLD_HUMAN_WELDING', 0, true) + Wait(20000) + ClearPedTasksImmediately(playerPed) + SetVehicleDoorsLocked(vehicle, 1) + SetVehicleDoorsLockedForAllPlayers(vehicle, false) + ESX.ShowNotification(TranslateCap('vehicle_unlocked')) + end + elseif action == 'impound' then + if A.currentTask.busy then return end + ESX.ShowHelpNotification(TranslateCap('impound_prompt')) + TaskStartScenarioInPlace(playerPed, 'CODE_HUMAN_MEDIC_TEND_TO_DEAD', 0, true) + A.currentTask.busy = true + A.currentTask.task = ESX.SetTimeout(10000, function() + ClearPedTasks(playerPed) + ImpoundVehicle(vehicle) + Wait(100) + end) + CreateThread(function() + while A.currentTask.busy do + Wait(1000) + vehicle = GetClosestVehicle(coords.x, coords.y, coords.z, 3.0, 0, 71) + if not DoesEntityExist(vehicle) and A.currentTask.busy then + ESX.ShowNotification(TranslateCap('impound_canceled_moved')) + ESX.ClearTimeout(A.currentTask.task) + ClearPedTasks(playerPed) + A.currentTask.busy = false + break + end + end + end) + end + else + ESX.ShowNotification(TranslateCap('no_vehicles_nearby')) + end + end, function(menu) + OpenPoliceActionsMenu() + end) + + elseif data.current.value == "object_spawner" then + local elements4 = { + {unselectable = true, icon = "fas fa-object", title = element.title}, + {icon = "fas fa-cone", title = TranslateCap('cone'), model = 'prop_roadcone02a'}, + {icon = "fas fa-cone", title = TranslateCap('barrier'), model = 'prop_barrier_work05'}, + {icon = "fas fa-cone", title = TranslateCap('spikestrips'), model = 'p_ld_stinger_s'}, + {icon = "fas fa-cone", title = TranslateCap('box'), model = 'prop_boxpile_07d'}, + {icon = "fas fa-cone", title = TranslateCap('cash'), model = 'hei_prop_cash_crate_half_full'} + } + + ESX.OpenContext("right", elements4, function(menu4,element4) + local data2 = {current = element4} + local playerPed = PlayerPedId() + local coords, forward = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed) + local objectCoords = (coords + forward * 1.0) + ESX.Game.SpawnObject(data2.current.model, objectCoords, function(obj) + Wait(100) + SetEntityHeading(obj, GetEntityHeading(playerPed)) + PlaceObjectOnGroundProperly(obj) + end) + end, function(menu) + OpenPoliceActionsMenu() + end) + end + end) +end diff --git a/[esx_addons]/esx_policejob/client/menus/armory.lua b/[esx_addons]/esx_policejob/client/menus/armory.lua new file mode 100644 index 00000000..abb0fa34 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/menus/armory.lua @@ -0,0 +1,278 @@ +local A = POLICE + +function OpenArmoryMenu(station) + if Config.OxInventory then + exports.ox_inventory:openInventory('stash', { id = 'society_police', owner = station }) + return ESX.CloseContext() + end + + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory')}, + {icon = "fas fa-gun", title = TranslateCap('buy_weapons'), value = 'buy_weapons'} + } + + if Config.EnableArmoryManagement then + table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('get_weapon'), value = 'get_weapon'}) + table.insert(elements, {icon = "fas fa-gun", title = TranslateCap('put_weapon'), value = 'put_weapon'}) + table.insert(elements, {icon = "fas fa-box", title = TranslateCap('remove_object'), value = 'get_stock'}) + table.insert(elements, {icon = "fas fa-box", title = TranslateCap('deposit_object'), value = 'put_stock'}) + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + if data.current.value == 'get_weapon' then + OpenGetWeaponMenu() + elseif data.current.value == 'put_weapon' then + OpenPutWeaponMenu() + elseif data.current.value == 'buy_weapons' then + OpenBuyWeaponsMenu() + elseif data.current.value == 'put_stock' then + OpenPutStocksMenu() + elseif data.current.value == 'get_stock' then + OpenGetStocksMenu() + end + end, function(menu) + POLICE.CurrentAction = 'menu_armory' + POLICE.CurrentActionMsg = TranslateCap('open_armory') + POLICE.CurrentActionData = {station = station} + end) +end + +function OpenGetWeaponMenu() + ESX.TriggerServerCallback('esx_policejob:getArmoryWeapons', function(weapons) + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('get_weapon_menu')} + } + for i=1, #weapons do + if weapons[i].count > 0 then + elements[#elements+1] = { + icon = "fas fa-gun", + title = 'x' .. weapons[i].count .. ' ' .. ESX.GetWeaponLabel(weapons[i].name), + value = weapons[i].name + } + end + end + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + ESX.TriggerServerCallback('esx_policejob:removeArmoryWeapon', function() + ESX.CloseContext() + OpenGetWeaponMenu() + end, data.current.value) + end) + end) +end + +function OpenPutWeaponMenu() + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('put_weapon_menu')} + } + local playerPed = PlayerPedId() + local weaponList = ESX.GetWeaponList() + + for i=1, #weaponList do + local weaponHash = joaat(weaponList[i].name) + if HasPedGotWeapon(playerPed, weaponHash, false) and weaponList[i].name ~= 'WEAPON_UNARMED' then + elements[#elements+1] = { + icon = "fas fa-gun", + title = weaponList[i].label, + value = weaponList[i].name + } + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + ESX.TriggerServerCallback('esx_policejob:addArmoryWeapon', function() + ESX.CloseContext() + OpenPutWeaponMenu() + end, data.current.value, true) + end) +end + +function OpenBuyWeaponsMenu() + local elements = { + {unselectable = true, icon = "fas fa-gun", title = TranslateCap('armory_weapontitle')} + } + local playerPed = PlayerPedId() + + for _,v in ipairs(Config.AuthorizedWeapons[ESX.PlayerData.job.grade_name]) do + local weaponNum, weapon = ESX.GetWeapon(v.weapon) + local components, label = {}, '' + local hasWeapon = HasPedGotWeapon(playerPed, joaat(v.weapon), false) + + if v.components then + for i=1, #v.components do + if v.components[i] then + local component = weapon.components[i] + local hasComponent = HasPedGotWeaponComponent(playerPed, joaat(v.weapon), component.hash) + if hasComponent then + label = ('%s: %s'):format(component.label, TranslateCap('armory_owned')) + else + if v.components[i] > 0 then + label = ('%s: %s'):format(component.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.components[i]))) + else + label = ('%s: %s'):format(component.label, TranslateCap('armory_free')) + end + end + components[#components+1] = { + icon = "fas fa-gun", + title = label, + componentLabel = component.label, + hash = component.hash, + name = component.name, + price = v.components[i], + hasComponent = hasComponent, + componentNum = i + } + end + end + end + + if hasWeapon and v.components then + label = ('%s: '):format(weapon.label) + elseif hasWeapon and not v.components then + label = ('%s: %s'):format(weapon.label, TranslateCap('armory_owned')) + else + if v.price > 0 then + label = ('%s: %s'):format(weapon.label, TranslateCap('armory_item', ESX.Math.GroupDigits(v.price))) + else + label = ('%s: %s'):format(weapon.label, TranslateCap('armory_free')) + end + end + + elements[#elements+1] = { + icon = "fas fa-gun", + title = label, + weaponLabel = weapon.label, + name = weapon.name, + components = components, + price = v.price, + hasWeapon = hasWeapon + } + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + if data.current.hasWeapon then + if #data.current.components > 0 then + OpenWeaponComponentShop(data.current.components, data.current.name, menu) + end + else + ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) + if bought then + if data.current.price > 0 then + ESX.ShowNotification(TranslateCap('armory_bought', data.current.weaponLabel, ESX.Math.GroupDigits(data.current.price))) + end + ESX.CloseContext() + OpenBuyWeaponsMenu() + else + ESX.ShowNotification(TranslateCap('armory_money')) + end + end, data.current.name, 1) + end + end) +end + +function OpenWeaponComponentShop(components, weaponName, parentShop) + ESX.OpenContext("right", components, function(menu,element) + local data = {current = element} + if data.current.hasComponent then + ESX.ShowNotification(TranslateCap('armory_hascomponent')) + else + ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought) + if bought then + if data.current.price > 0 then + ESX.ShowNotification(TranslateCap('armory_bought', data.current.componentLabel, ESX.Math.GroupDigits(data.current.price))) + end + ESX.CloseContext() + parentShop.close() + OpenBuyWeaponsMenu() + else + ESX.ShowNotification(TranslateCap('armory_money')) + end + end, weaponName, 2, data.current.componentNum) + end + end) +end + +function OpenGetStocksMenu() + ESX.TriggerServerCallback('esx_policejob:getStockItems', function(items) + local elements = { + {unselectable = true, icon = "fas fa-box", title = TranslateCap('police_stock')} + } + for i=1, #items do + elements[#elements+1] = { + icon = "fas fa-box", + title = 'x' .. items[i].count .. ' ' .. items[i].label, + value = items[i].name + } + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + local itemName = data.current.value + + local elements2 = { + {unselectable = true, icon = "fas fa-box", title = element.title}, + {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, + {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} + } + + ESX.OpenContext("right", elements2, function(menu2,element2) + local data2 = {value = menu2.eles[2].inputValue} + local count = tonumber(data2.value) + if not count then + ESX.ShowNotification(TranslateCap('quantity_invalid')) + else + ESX.CloseContext() + TriggerServerEvent('esx_policejob:getStockItem', itemName, count) + Wait(300) + OpenGetStocksMenu() + end + end) + end) + end) +end + +function OpenPutStocksMenu() + ESX.TriggerServerCallback('esx_policejob:getPlayerInventory', function(inventory) + local elements = { + {unselectable = true, icon = "fas fa-box", title = TranslateCap('inventory')} + } + for i=1, #inventory.items do + local item = inventory.items[i] + if item.count > 0 then + elements[#elements+1] = { + icon = "fas fa-box", + title = item.label .. ' x' .. item.count, + type = 'item_standard', + value = item.name + } + end + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + local itemName = data.current.value + + local elements2 = { + {unselectable = true, icon = "fas fa-box", title = element.title}, + {title = TranslateCap('quantity'), input = true, inputType = "number", inputMin = 1, inputMax = 150, inputPlaceholder = TranslateCap('quantity_placeholder')}, + {icon = "fas fa-check-double", title = TranslateCap('confirm'), value = "confirm"} + } + + ESX.OpenContext("right", elements2, function(menu2,element2) + local data2 = {value = menu2.eles[2].inputValue} + local count = tonumber(data2.value) + if not count then + ESX.ShowNotification(TranslateCap('quantity_invalid')) + else + ESX.CloseContext() + TriggerServerEvent('esx_policejob:putStockItems', itemName, count) + Wait(300) + OpenPutStocksMenu() + end + end) + end) + end) +end diff --git a/[esx_addons]/esx_policejob/client/menus/cloakroom.lua b/[esx_addons]/esx_policejob/client/menus/cloakroom.lua new file mode 100644 index 00000000..9c1cd2ba --- /dev/null +++ b/[esx_addons]/esx_policejob/client/menus/cloakroom.lua @@ -0,0 +1,139 @@ +local A = POLICE + +function OpenCloakroomMenu() + local playerPed = PlayerPedId() + local grade = ESX.PlayerData.job.grade_name + + local elements = { + {unselectable = true, icon = "fas fa-shirt", title = TranslateCap("cloakroom")}, + {icon = "fas fa-shirt", title = TranslateCap('citizen_wear'), value = 'citizen_wear'}, + {icon = "fas fa-shirt", title = TranslateCap('bullet_wear'), uniform = 'bullet_wear'}, + {icon = "fas fa-shirt", title = TranslateCap('gilet_wear'), uniform = 'gilet_wear'}, + {icon = "fas fa-shirt", title = TranslateCap('police_wear'), uniform = grade} + } + + if Config.EnableCustomPeds then + if Config.CustomPeds and Config.CustomPeds.shared then + for _,v in ipairs(Config.CustomPeds.shared) do + elements[#elements+1] = { + icon = "fas fa-shirt", + title = v.label, + value = 'freemode_ped', + maleModel = v.maleModel, + femaleModel = v.femaleModel + } + end + end + if Config.CustomPeds and Config.CustomPeds[grade] then + for _,v in ipairs(Config.CustomPeds[grade]) do + elements[#elements+1] = { + icon = "fas fa-shirt", + title = v.label, + value = 'freemode_ped', + maleModel = v.maleModel, + femaleModel = v.femaleModel + } + end + end + end + + ESX.OpenContext("right", elements, function(menu,element) + POLICE.cleanPlayer(playerPed) + local data = {current = element} + + if data.current.value == 'citizen_wear' then + if Config.EnableCustomPeds then + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) + local isMale = skin.sex == 0 + TriggerEvent('skinchanger:loadDefaultModel', isMale, function() + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin2) + TriggerEvent('skinchanger:loadSkin', skin2) + TriggerEvent('esx:restoreLoadout') + end) + end) + end) + else + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) + TriggerEvent('skinchanger:loadSkin', skin) + end) + end + + if Config.EnableESXService then + ESX.TriggerServerCallback('esx_service:isInService', function(isInService) + if isInService then + POLICE.playerInService = false + local notification = { + title = TranslateCap('service_anonunce'), + subject = '', + msg = TranslateCap('service_out_announce', GetPlayerName(PlayerId())), + iconType = 1 + } + TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') + TriggerServerEvent('esx_service:disableService', 'police') + ESX.ShowNotification(TranslateCap('service_out')) + end + end, 'police') + end + end + + if Config.EnableESXService and data.current.value ~= 'citizen_wear' then + local awaitService + ESX.TriggerServerCallback('esx_service:isInService', function(isInService) + if not isInService then + if Config.MaxInService ~= -1 then + ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount) + if not canTakeService then + ESX.ShowNotification(TranslateCap('service_max', inServiceCount, maxInService)) + else + awaitService = true + POLICE.playerInService = true + local notification = { + title = TranslateCap('service_anonunce'), + subject = '', + msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), + iconType = 1 + } + TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') + ESX.ShowNotification(TranslateCap('service_in')) + end + end, 'police') + else + awaitService = true + POLICE.playerInService = true + local notification = { + title = TranslateCap('service_anonunce'), + subject = '', + msg = TranslateCap('service_in_announce', GetPlayerName(PlayerId())), + iconType = 1 + } + TriggerServerEvent('esx_service:notifyAllInService', notification, 'police') + ESX.ShowNotification(TranslateCap('service_in')) + end + else + awaitService = true + end + end, 'police') + + while awaitService == nil do Wait(0) end + if not awaitService then return end + end + + if data.current.uniform then + POLICE.setUniform(data.current.uniform, playerPed) + elseif data.current.value == 'freemode_ped' then + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin) + local modelHash = skin.sex == 0 and joaat(data.current.maleModel) or joaat(data.current.femaleModel) + ESX.Streaming.RequestModel(modelHash, function() + SetPlayerModel(PlayerId(), modelHash) + SetModelAsNoLongerNeeded(modelHash) + SetPedDefaultComponentVariation(PlayerPedId()) + TriggerEvent('esx:restoreLoadout') + end) + end) + end + end, function(menu) + POLICE.CurrentAction = 'menu_cloakroom' + POLICE.CurrentActionMsg = TranslateCap('open_cloackroom') + POLICE.CurrentActionData = {} + end) +end diff --git a/[esx_addons]/esx_policejob/client/menus/fines.lua b/[esx_addons]/esx_policejob/client/menus/fines.lua new file mode 100644 index 00000000..72093582 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/menus/fines.lua @@ -0,0 +1,90 @@ +local A = POLICE +local fineList = {} + +function OpenFineMenu(player) + if Config.EnableFinePresets then + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('fine')}, + {icon = "fas fa-scroll", title = TranslateCap('traffic_offense'), value = 0}, + {icon = "fas fa-scroll", title = TranslateCap('minor_offense'), value = 1}, + {icon = "fas fa-scroll", title = TranslateCap('average_offense'), value = 2}, + {icon = "fas fa-scroll", title = TranslateCap('major_offense'), value = 3} + } + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + OpenFineCategoryMenu(player, data.current.value) + end) + else + ESX.CloseContext() + OpenFineTextInput(player) + end +end + +function OpenFineCategoryMenu(player, category) + if not fineList[category] then + local p = promise.new() + ESX.TriggerServerCallback('esx_policejob:getFineList', function(fines) + p:resolve(fines) + end, category) + fineList[category] = Citizen.Await(p) + end + + local elements = { + {unselectable = true, icon = "fas fa-scroll", title = TranslateCap('fine')} + } + + for _,fine in ipairs(fineList[category]) do + elements[#elements+1] = { + icon = "fas fa-scroll", + title = ('%s %s'):format(fine.label, TranslateCap('armory_item', ESX.Math.GroupDigits(fine.amount))), + value = fine.id, + amount = fine.amount, + fineLabel = fine.label + } + end + + ESX.OpenContext("right", elements, function(menu,element) + local data = {current = element} + if Config.EnablePlayerManagement then + TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) + else + TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), '', TranslateCap('fine_total', data.current.fineLabel), data.current.amount) + end + + ESX.SetTimeout(300, function() + OpenFineCategoryMenu(player, category) + end) + end) +end + +function OpenFineTextInput(player) + Citizen.CreateThread(function() + local amount = 0 + local reason = '' + AddTextEntry('FMMC_KEY_TIP1', TranslateCap('fine_enter_amount')) + Citizen.Wait(0) + DisplayOnscreenKeyboard(1, 'FMMC_KEY_TIP1', '', '', '', '', '', 30) + while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do + Citizen.Wait(0) + end + if UpdateOnscreenKeyboard() ~= 2 then + amount = tonumber(GetOnscreenKeyboardResult()) + if amount == nil or amount <= 0 then + ESX.ShowNotification(TranslateCap('invalid_amount')) + return + end + end + AddTextEntry('FMMC_KEY_TIP1', TranslateCap('fine_enter_text')) + Citizen.Wait(0) + DisplayOnscreenKeyboard(1, 'FMMC_KEY_TIP1', '', '', '', '', '', 120) + while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do + Citizen.Wait(0) + end + if UpdateOnscreenKeyboard() ~= 2 then + reason = GetOnscreenKeyboardResult() + end + Citizen.Wait(500) + TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', reason, amount) + OpenPoliceActionsMenu() + end) +end diff --git a/[esx_addons]/esx_policejob/client/objects.lua b/[esx_addons]/esx_policejob/client/objects.lua new file mode 100644 index 00000000..44455a53 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/objects.lua @@ -0,0 +1,13 @@ +local A = POLICE + +function POLICE.SpawnPoliceObject(model) + local playerPed = PlayerPedId() + local coords, forward = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed) + local objectCoords = (coords + forward * 1.0) + + ESX.Game.SpawnObject(model, objectCoords, function(obj) + Wait(100) + SetEntityHeading(obj, GetEntityHeading(playerPed)) + PlaceObjectOnGroundProperly(obj) + end) +end diff --git a/[esx_addons]/esx_policejob/client/phone.lua b/[esx_addons]/esx_policejob/client/phone.lua new file mode 100644 index 00000000..a6ff5e72 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/phone.lua @@ -0,0 +1,19 @@ +local A = POLICE + +RegisterNetEvent('esx_phone:loaded') +AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts) + local specialContact = { + name = TranslateCap('phone_police'), + number = 'police', + base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDFGQTJDRkI0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDFGQTJDRkM0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0MUZBMkNGOTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0MUZBMkNGQTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoW66EYAAAjG...' + } + TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon) +end) + +AddEventHandler('esx_phone:cancelMessage', function(dispatchNumber) + if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and dispatchNumber == 'police' then + if Config.EnableESXService and not A.playerInService then + CancelEvent() + end + end +end) diff --git a/[esx_addons]/esx_policejob/client/state.lua b/[esx_addons]/esx_policejob/client/state.lua new file mode 100644 index 00000000..4e83dbb6 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/state.lua @@ -0,0 +1,21 @@ +POLICE = POLICE or {} + +POLICE.CurrentActionData = {} +POLICE.handcuffTimer = { active = false, task = nil } +POLICE.dragStatus = { isDragged = false, CopId = nil } +POLICE.blipsCops = {} +POLICE.currentTask = {} + +POLICE.HasAlreadyEnteredMarker = false +POLICE.isDead = false +POLICE.isHandcuffed = false +POLICE.hasAlreadyJoined = false +POLICE.playerInService = false + +POLICE.LastStation, POLICE.LastPart, POLICE.LastPartNum = nil, nil, nil +POLICE.LastEntity = nil +POLICE.CurrentAction = nil +POLICE.CurrentActionMsg = nil + +POLICE.ARREST_DICT, POLICE.ARREST_ANIM = 'mp_arresting', 'idle' +POLICE.isInShopMenu = false diff --git a/[esx_addons]/esx_policejob/client/utils.lua b/[esx_addons]/esx_policejob/client/utils.lua new file mode 100644 index 00000000..bfa236f5 --- /dev/null +++ b/[esx_addons]/esx_policejob/client/utils.lua @@ -0,0 +1,51 @@ +local A = POLICE + +function A.playCuffAnim(ped) + RequestAnimDict(A.ARREST_DICT) + local tries = 0 + while not HasAnimDictLoaded(A.ARREST_DICT) and tries < 100 do + Wait(10); tries = tries + 1 + end + TaskPlayAnim(ped, A.ARREST_DICT, A.ARREST_ANIM, 8.0, -8.0, -1, 49, 0.0, false, false, false) +end + +function A.stopCuffAnim(ped) + ClearPedSecondaryTask(ped) + if HasAnimDictLoaded(A.ARREST_DICT) then + RemoveAnimDict(A.ARREST_DICT) + end +end + +function A.cleanPlayer(playerPed) + SetPedArmour(playerPed, 0) + ClearPedBloodDamage(playerPed) + ResetPedVisibleDamage(playerPed) + ClearPedLastWeaponDamage(playerPed) + ResetPedMovementClipset(playerPed, 0) +end + +function POLICE.setUniform(uniform, playerPed) + TriggerEvent('skinchanger:getSkin', function(skin) + local uniformObject + local sex = (skin.sex == 0) and "male" or "female" + + if Config.Uniforms and Config.Uniforms[uniform] and Config.Uniforms[uniform][sex] then + uniformObject = Config.Uniforms[uniform][sex] + end + + if uniformObject then + TriggerEvent('skinchanger:loadClothes', skin, uniformObject) + if uniform == 'bullet_wear' then + SetPedArmour(playerPed, 100) + end + else + ESX.ShowNotification(TranslateCap('no_outfit')) + end + end) +end + +function ImpoundVehicle(vehicle) + ESX.Game.DeleteVehicle(vehicle) + ESX.ShowNotification(TranslateCap('impound_successful')) + A.currentTask.busy = false +end diff --git a/[esx_addons]/esx_policejob/fxmanifest.lua b/[esx_addons]/esx_policejob/fxmanifest.lua index 851f635d..d5addec3 100644 --- a/[esx_addons]/esx_policejob/fxmanifest.lua +++ b/[esx_addons]/esx_policejob/fxmanifest.lua @@ -19,10 +19,21 @@ server_scripts { } client_scripts { - '@es_extended/locale.lua', - 'locales/*.lua', - 'shared/config.lua', - 'client/*.lua' + '@es_extended/locale.lua', + 'locales/*.lua', + 'shared/config.lua', + 'client/state.lua', + 'client/boot.lua', + 'client/main.lua', + 'client/vehicle.lua', + 'client/cuffs.lua', + 'client/interactions/*.lua', + 'client/menus/*.lua', + 'client/markers.lua', + 'client/blips.lua', + 'client/objects.lua', + 'client/inputs.lua', + 'client/phone.lua' } dependencies {