mirror of
https://github.com/esx-framework/esx_core.git
synced 2026-08-31 10:18:54 +00:00
1568 lines
46 KiB
Lua
1568 lines
46 KiB
Lua
ESX.Modules['job_police'] = {}
|
|
local self = ESX.Modules['job_police']
|
|
|
|
-- Properties
|
|
self.Config = ESX.EvalFile(GetCurrentResourceName(), 'modules/job_police/data/config.lua', {
|
|
vector3 = vector3
|
|
})['Config']
|
|
|
|
self.CurrentActionData = {}
|
|
self.HandcuffTimer = {}
|
|
self.DragStatus = {isDragged = false}
|
|
self.BlipsCops = {}
|
|
self.CurrentTask = {}
|
|
self.HasAlreadyEnteredMarker = false
|
|
self.IsDead = false
|
|
self.IsHandcuffed = false
|
|
self.HasAlreadyJoined = false
|
|
self.PlayerInService = false
|
|
self.LastStation = nil
|
|
self.LastPart = nil
|
|
self.LastPartNum = nil
|
|
self.LastEntity = nil
|
|
self.CurrentAction = nil
|
|
self.CurrentActionMsg = nil
|
|
self.IsInShopMenu = false
|
|
self.SpawnedVehicles = {}
|
|
|
|
-- Locals
|
|
local Interact = ESX.Modules['interact']
|
|
local Input = ESX.Modules['input']
|
|
|
|
-- Methods
|
|
self.Init = function()
|
|
|
|
self.RegisterControls()
|
|
|
|
local translations = ESX.EvalFile(GetCurrentResourceName(), 'modules/job_police/data/locales/' .. Config.Locale .. '.lua')['Translations']
|
|
LoadLocale('job_police', Config.Locale, translations)
|
|
|
|
for stationName, station in pairs(self.Config.PoliceStations) do
|
|
|
|
-- Blip
|
|
local blip = AddBlipForCoord(station.Blip.Coords)
|
|
|
|
SetBlipSprite (blip, station.Blip.Sprite)
|
|
SetBlipDisplay(blip, station.Blip.Display)
|
|
SetBlipScale (blip, station.Blip.Scale)
|
|
SetBlipColour (blip, station.Blip.Colour)
|
|
SetBlipAsShortRange(blip, true)
|
|
|
|
BeginTextCommandSetBlipName('STRING')
|
|
AddTextComponentSubstringPlayerName(_U('job_police:map_blip'))
|
|
EndTextCommandSetBlipName(blip)
|
|
|
|
-- Cloakrooms
|
|
for i=1, #station.Cloakrooms, 1 do
|
|
|
|
local cloakroom = station.Cloakrooms[i]
|
|
local key = 'job_police:' .. stationName .. 'cloakroom:' .. tostring(i)
|
|
|
|
Interact.Register({
|
|
name = key,
|
|
type = 'marker',
|
|
distance = self.Config.DrawDistance,
|
|
radius = 2.0,
|
|
pos = cloakroom,
|
|
size = 1.0,
|
|
mtype = 20,
|
|
color = self.Config.MarkerColor,
|
|
rotate = true,
|
|
check = self.IsPolice,
|
|
})
|
|
|
|
AddEventHandler('esx:interact:enter:' .. key, function(data)
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:open_cloackroom'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenCloakroomMenu(station)
|
|
end
|
|
|
|
end)
|
|
|
|
AddEventHandler('esx:interact:exit:' .. key, function(data)
|
|
self.CurrentAction = nil
|
|
end)
|
|
|
|
end
|
|
|
|
-- Armories
|
|
for i=1, #station.Armories, 1 do
|
|
|
|
local armory = station.Armories[i]
|
|
local key = 'job_police:' .. stationName .. ':armory:' .. tostring(i)
|
|
|
|
Interact.Register({
|
|
name = key,
|
|
type = 'marker',
|
|
distance = self.Config.DrawDistance,
|
|
radius = 1.0,
|
|
pos = armory,
|
|
size = 0.5,
|
|
mtype = 21,
|
|
color = self.Config.MarkerColor,
|
|
rotate = true,
|
|
check = self.IsPolice,
|
|
})
|
|
|
|
AddEventHandler('esx:interact:enter:' .. key, function(data)
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:open_armory'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenArmoryMenu(station)
|
|
end
|
|
|
|
end)
|
|
|
|
AddEventHandler('esx:interact:exit:' .. key, function(data)
|
|
self.CurrentAction = nil
|
|
end)
|
|
|
|
end
|
|
|
|
-- Vehicles
|
|
for i=1, #station.Vehicles, 1 do
|
|
|
|
local vehicle = station.Vehicles[i]
|
|
local key = 'job_police:' .. stationName .. ':garage:' .. tostring(i)
|
|
|
|
Interact.Register({
|
|
name = key,
|
|
type = 'marker',
|
|
distance = self.Config.DrawDistance,
|
|
radius = 2.0,
|
|
pos = vehicle.Spawner,
|
|
size = 1.0,
|
|
mtype = 36,
|
|
color = self.Config.MarkerColor,
|
|
rotate = true,
|
|
check = self.IsPolice,
|
|
config = vehicle
|
|
})
|
|
|
|
AddEventHandler('esx:interact:enter:' .. key, function(data)
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:garage_prompt'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenVehicleSpawnerMenu('car', data.config)
|
|
end
|
|
|
|
end)
|
|
|
|
AddEventHandler('esx:interact:exit:' .. key, function(data)
|
|
self.CurrentAction = nil
|
|
end)
|
|
|
|
end
|
|
|
|
-- Helicopters
|
|
for i=1, #station.Helicopters, 1 do
|
|
|
|
local heli = station.Helicopters[i]
|
|
local key = 'job_police:' .. stationName .. ':garage_heli:' .. tostring(i)
|
|
|
|
Interact.Register({
|
|
name = key,
|
|
type = 'marker',
|
|
distance = self.Config.DrawDistance,
|
|
radius = 2.0,
|
|
pos = heli.Spawner,
|
|
size = 1.0,
|
|
mtype = 36,
|
|
color = self.Config.MarkerColor,
|
|
rotate = true,
|
|
check = self.IsPolice,
|
|
config = heli
|
|
})
|
|
|
|
AddEventHandler('esx:interact:enter:' .. key, function(data)
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:helicopter_prompt'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenVehicleSpawnerMenu('helicopter', data.config)
|
|
end
|
|
|
|
end)
|
|
|
|
AddEventHandler('esx:interact:exit:' .. key, function(data)
|
|
self.CurrentAction = nil
|
|
end)
|
|
|
|
end
|
|
|
|
-- Boss actions
|
|
for i=1, #station.BossActions, 1 do
|
|
|
|
local actions = station.BossActions[i]
|
|
local key = 'job_police:' .. stationName .. ':bossactions:' .. tostring(i)
|
|
|
|
Interact.Register({
|
|
name = key,
|
|
type = 'marker',
|
|
distance = self.Config.DrawDistance,
|
|
radius = 2.0,
|
|
pos = actions,
|
|
size = 1.0,
|
|
mtype = 22,
|
|
color = self.Config.MarkerColor,
|
|
rotate = true,
|
|
check = self.IsPoliceBoss,
|
|
})
|
|
|
|
AddEventHandler('esx:interact:enter:' .. key, function(data)
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:open_bossmenu'))
|
|
|
|
local bossAction
|
|
|
|
bossAction = function()
|
|
|
|
TriggerEvent('esx_society:openBossMenu', 'police', function(data, menu)
|
|
menu.close()
|
|
self.CurrentAction = bossAction
|
|
end, {
|
|
wash = false
|
|
})
|
|
|
|
end
|
|
|
|
self.CurrentAction = bossAction
|
|
|
|
end)
|
|
|
|
AddEventHandler('esx:interact:exit:' .. key, function(data)
|
|
self.CurrentAction = nil
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self.RegisterControls = function()
|
|
|
|
Input.RegisterControl(Input.Groups.MOVE, Input.Controls.PICKUP)
|
|
Input.RegisterControl(Input.Groups.MOVE, Input.Controls.SELECT_CHARACTER_FRANKLIN)
|
|
Input.RegisterControl(Input.Groups.MOVE, Input.Controls.PICKUP)
|
|
|
|
end
|
|
|
|
self.IsPolice = function(playerPed, coords)
|
|
return ESX.PlayerData.job and (ESX.PlayerData.job.name == 'police')
|
|
end
|
|
|
|
self.IsPoliceBoss = function(playerPed, coords)
|
|
return self.IsPolice() and (ESX.PlayerData.job.grade_name == 'boss')
|
|
end
|
|
|
|
self.CleanPlayer = function(playerPed)
|
|
SetPedArmour(playerPed, 0)
|
|
ClearPedBloodDamage(playerPed)
|
|
ResetPedVisibleDamage(playerPed)
|
|
ClearPedLastWeaponDamage(playerPed)
|
|
ResetPedMovementClipset(playerPed, 0)
|
|
end
|
|
|
|
self.SetUniform = function(uniform, playerPed)
|
|
|
|
TriggerEvent('skinchanger:getSkin', function(skin)
|
|
|
|
local uniformObject
|
|
|
|
if skin.sex == 0 then
|
|
uniformObject = self.Config.Uniforms[uniform].male
|
|
else
|
|
uniformObject = self.Config.Uniforms[uniform].female
|
|
end
|
|
|
|
if uniformObject then
|
|
TriggerEvent('skinchanger:loadClothes', skin, uniformObject)
|
|
|
|
if uniform == 'bullet_wear' then
|
|
SetPedArmour(playerPed, 100)
|
|
end
|
|
else
|
|
ESX.ShowNotification(_U('job_police:no_outfit'))
|
|
end
|
|
end)
|
|
|
|
end
|
|
|
|
self.OpenCloakroomMenu = function()
|
|
|
|
local playerPed = PlayerPedId()
|
|
local grade = ESX.PlayerData.job.grade_name
|
|
|
|
local elements = {
|
|
{label = _U('job_police:citizen_wear'), value = 'citizen_wear'},
|
|
{label = _U('job_police:bullet_wear'), uniform = 'bullet_wear'},
|
|
{label = _U('job_police:gilet_wear'), uniform = 'gilet_wear'},
|
|
{label = _U('job_police:police_wear'), uniform = grade}
|
|
}
|
|
|
|
if self.Config.EnableCustomPeds then
|
|
for k,v in ipairs(Config.CustomPeds.shared) do
|
|
table.insert(elements, {label = v.label, value = 'freemode_ped', maleModel = v.maleModel, femaleModel = v.femaleModel})
|
|
end
|
|
|
|
for k,v in ipairs(Config.CustomPeds[grade]) do
|
|
table.insert(elements, {label = v.label, value = 'freemode_ped', maleModel = v.maleModel, femaleModel = v.femaleModel})
|
|
end
|
|
end
|
|
|
|
ESX.UI.Menu.CloseAll()
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'cloakroom', {
|
|
title = _U('job_police:cloakroom'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
|
|
menu.close()
|
|
|
|
self.CleanPlayer(playerPed)
|
|
|
|
if data.current.value == 'citizen_wear' then
|
|
if self.Config.EnableNonFreemodePeds 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 self.Config.EnableESXService then
|
|
ESX.TriggerServerCallback('esx_service:isInService', function(isInService)
|
|
if isInService then
|
|
PlayerInService = false
|
|
|
|
local notification = {
|
|
title = _U('job_police:service_anonunce'),
|
|
subject = '',
|
|
msg = _U('job_police:service_out_announce', GetPlayerName(PlayerId())),
|
|
iconType = 1
|
|
}
|
|
|
|
TriggerServerEvent('esx_service:notifyAllInService', notification, 'police')
|
|
|
|
TriggerServerEvent('esx_service:disableService', 'police')
|
|
TriggerEvent('esx_policejob:updateBlip')
|
|
ESX.ShowNotification(_U('job_police:service_out'))
|
|
end
|
|
end, 'police')
|
|
end
|
|
end
|
|
|
|
if self.Config.EnableESXService and data.current.value ~= 'citizen_wear' then
|
|
local awaitService
|
|
|
|
ESX.TriggerServerCallback('esx_service:isInService', function(isInService)
|
|
if not isInService then
|
|
|
|
ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount)
|
|
if not canTakeService then
|
|
ESX.ShowNotification(_U('job_police:service_max', inServiceCount, maxInService))
|
|
else
|
|
awaitService = true
|
|
PlayerInService = true
|
|
|
|
local notification = {
|
|
title = _U('job_police:service_anonunce'),
|
|
subject = '',
|
|
msg = _U('job_police:service_in_announce', GetPlayerName(PlayerId())),
|
|
iconType = 1
|
|
}
|
|
|
|
TriggerServerEvent('esx_service:notifyAllInService', notification, 'police')
|
|
TriggerEvent('esx_policejob:updateBlip')
|
|
ESX.ShowNotification(_U('job_police:service_in'))
|
|
end
|
|
end, 'police')
|
|
|
|
else
|
|
awaitService = true
|
|
end
|
|
end, 'police')
|
|
|
|
while awaitService == nil do
|
|
Citizen.Wait(5)
|
|
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
|
|
self.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 = GetHashKey(data.current.maleModel)
|
|
else
|
|
modelHash = GetHashKey(data.current.femaleModel)
|
|
end
|
|
|
|
ESX.Streaming.RequestModel(modelHash, function()
|
|
SetPlayerModel(PlayerId(), modelHash)
|
|
SetModelAsNoLongerNeeded(modelHash)
|
|
SetPedDefaultComponentVariation(PlayerPedId())
|
|
|
|
TriggerEvent('esx:restoreLoadout')
|
|
end)
|
|
end)
|
|
end
|
|
end, function(data, menu)
|
|
|
|
menu.close()
|
|
|
|
end, nil, function()
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:open_cloackroom'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenCloakroomMenu(station)
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
self.OpenArmoryMenu = function(station)
|
|
local elements = {
|
|
{label = _U('job_police:buy_weapons'), value = 'buy_weapons'}
|
|
}
|
|
|
|
if self.Config.EnableArmoryManagement then
|
|
table.insert(elements, {label = _U('job_police:get_weapon'), value = 'get_weapon'})
|
|
table.insert(elements, {label = _U('job_police:put_weapon'), value = 'put_weapon'})
|
|
table.insert(elements, {label = _U('job_police:remove_object'), value = 'get_stock'})
|
|
table.insert(elements, {label = _U('job_police:deposit_object'), value = 'put_stock'})
|
|
end
|
|
|
|
ESX.UI.Menu.CloseAll()
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory', {
|
|
title = _U('job_police:armory'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
|
|
if data.current.value == 'get_weapon' then
|
|
self.OpenGetWeaponMenu()
|
|
elseif data.current.value == 'put_weapon' then
|
|
self.OpenPutWeaponMenu()
|
|
elseif data.current.value == 'buy_weapons' then
|
|
self.OpenBuyWeaponsMenu()
|
|
elseif data.current.value == 'put_stock' then
|
|
self.OpenPutStocksMenu()
|
|
elseif data.current.value == 'get_stock' then
|
|
self.OpenGetStocksMenu()
|
|
end
|
|
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end, nil, function()
|
|
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:open_armory'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenArmoryMenu(station)
|
|
end
|
|
|
|
return true
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
self.OpenPoliceActionsMenu = function()
|
|
ESX.UI.Menu.CloseAll()
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'police_actions', {
|
|
title = 'Police',
|
|
align = 'top-left',
|
|
elements = {
|
|
{label = _U('job_police:citizen_interaction'), value = 'citizen_interaction'},
|
|
{label = _U('job_police:vehicle_interaction'), value = 'vehicle_interaction'},
|
|
{label = _U('job_police:object_spawner'), value = 'object_spawner'}
|
|
}}, function(data, menu)
|
|
if data.current.value == 'citizen_interaction' then
|
|
local elements = {
|
|
{label = _U('job_police:id_card'), value = 'identity_card'},
|
|
{label = _U('job_police:search'), value = 'search'},
|
|
{label = _U('job_police:handcuff'), value = 'handcuff'},
|
|
{label = _U('job_police:drag'), value = 'drag'},
|
|
{label = _U('job_police:put_in_vehicle'), value = 'put_in_vehicle'},
|
|
{label = _U('job_police:out_the_vehicle'), value = 'out_the_vehicle'},
|
|
{label = _U('job_police:fine'), value = 'fine'},
|
|
{label = _U('job_police:unpaid_bills'), value = 'unpaid_bills'}
|
|
}
|
|
|
|
if self.Config.EnableLicenses then
|
|
table.insert(elements, {label = _U('job_police:license_check'), value = 'license'})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'citizen_interaction', {
|
|
title = _U('job_police:citizen_interaction'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data2, menu2)
|
|
local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer()
|
|
if closestPlayer ~= -1 and closestDistance <= 3.0 then
|
|
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(_U('job_police:no_players_nearby'))
|
|
end
|
|
end, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
elseif data.current.value == 'vehicle_interaction' then
|
|
local elements = {}
|
|
local playerPed = PlayerPedId()
|
|
local vehicle = ESX.Game.GetVehicleInDirection()
|
|
|
|
if DoesEntityExist(vehicle) then
|
|
table.insert(elements, {label = _U('job_police:vehicle_info'), value = 'vehicle_infos'})
|
|
table.insert(elements, {label = _U('job_police:pick_lock'), value = 'hijack_vehicle'})
|
|
table.insert(elements, {label = _U('job_police:impound'), value = 'impound'})
|
|
end
|
|
|
|
table.insert(elements, {label = _U('job_police:search_database'), value = 'search_database'})
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_interaction', {
|
|
title = _U('job_police:vehicle_interaction'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data2, menu2)
|
|
local coords = GetEntityCoords(playerPed)
|
|
vehicle = ESX.Game.GetVehicleInDirection()
|
|
action = data2.current.value
|
|
|
|
if action == 'search_database' then
|
|
LookupVehicle()
|
|
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)
|
|
Citizen.Wait(20000)
|
|
ClearPedTasksImmediately(playerPed)
|
|
|
|
SetVehicleDoorsLocked(vehicle, 1)
|
|
SetVehicleDoorsLockedForAllPlayers(vehicle, false)
|
|
ESX.ShowNotification(_U('job_police:vehicle_unlocked'))
|
|
end
|
|
elseif action == 'impound' then
|
|
-- is the script busy?
|
|
if CurrentTask.busy then
|
|
return
|
|
end
|
|
|
|
ESX.ShowHelpNotification(_U('job_police: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)
|
|
Citizen.Wait(100) -- sleep the entire script to let stuff sink back to reality
|
|
end)
|
|
|
|
-- keep track of that vehicle!
|
|
Citizen.CreateThread(function()
|
|
while CurrentTask.busy do
|
|
Citizen.Wait(1000)
|
|
|
|
vehicle = GetClosestVehicle(coords.x, coords.y, coords.z, 3.0, 0, 71)
|
|
if not DoesEntityExist(vehicle) and CurrentTask.busy then
|
|
ESX.ShowNotification(_U('job_police:impound_canceled_moved'))
|
|
ESX.ClearTimeout(CurrentTask.task)
|
|
ClearPedTasks(playerPed)
|
|
CurrentTask.busy = false
|
|
break
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
else
|
|
ESX.ShowNotification(_U('job_police:no_vehicles_nearby'))
|
|
end
|
|
|
|
end)
|
|
|
|
elseif data.current.value == 'object_spawner' then
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'citizen_interaction', {
|
|
title = _U('job_police:traffic_interaction'),
|
|
align = 'top-left',
|
|
elements = {
|
|
{label = _U('job_police:cone'), model = 'prop_roadcone02a'},
|
|
{label = _U('job_police:barrier'), model = 'prop_barrier_work05'},
|
|
{label = _U('job_police:spikestrips'), model = 'p_ld_stinger_s'},
|
|
{label = _U('job_police:box'), model = 'prop_boxpile_07d'},
|
|
{label = _U('job_police:cash'), model = 'hei_prop_cash_crate_half_full'}
|
|
}}, function(data2, menu2)
|
|
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)
|
|
SetEntityHeading(obj, GetEntityHeading(playerPed))
|
|
PlaceObjectOnGroundProperly(obj)
|
|
end)
|
|
end, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
end
|
|
end)
|
|
|
|
end
|
|
|
|
self.OpenIdentityCardMenu = function(player)
|
|
ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data)
|
|
local elements = {
|
|
{label = _U('job_police:name', data.name)},
|
|
{label = _U('job_police:job', ('%s - %s'):format(data.job, data.grade))}
|
|
}
|
|
|
|
if self.Config.EnableESXIdentity then
|
|
table.insert(elements, {label = _U('job_police:sex', _U(data.sex))})
|
|
table.insert(elements, {label = _U('job_police:dob', data.dob)})
|
|
table.insert(elements, {label = _U('job_police:height', data.height)})
|
|
end
|
|
|
|
if data.drunk then
|
|
table.insert(elements, {label = _U('job_police:bac', data.drunk)})
|
|
end
|
|
|
|
if data.licenses then
|
|
table.insert(elements, {label = _U('job_police:license_label')})
|
|
|
|
for i=1, #data.licenses, 1 do
|
|
table.insert(elements, {label = data.licenses[i].label})
|
|
end
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'citizen_interaction', {
|
|
title = _U('job_police:citizen_interaction'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, nil, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end, GetPlayerServerId(player))
|
|
end
|
|
|
|
self.OpenBodySearchMenu = function(player)
|
|
ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data)
|
|
local elements = {}
|
|
|
|
for i=1, #data.accounts, 1 do
|
|
if data.accounts[i].name == 'black_money' and data.accounts[i].money > 0 then
|
|
table.insert(elements, {
|
|
label = _U('job_police: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 = _U('job_police:guns_label')})
|
|
|
|
for i=1, #data.weapons, 1 do
|
|
table.insert(elements, {
|
|
label = _U('job_police: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
|
|
|
|
table.insert(elements, {label = _U('job_police:inventory_label')})
|
|
|
|
for i=1, #data.inventory, 1 do
|
|
if data.inventory[i].count > 0 then
|
|
table.insert(elements, {
|
|
label = _U('job_police: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.UI.Menu.Open('default', GetCurrentResourceName(), 'body_search', {
|
|
title = _U('job_police:search'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
if data.current.value then
|
|
TriggerServerEvent('esx_policejob:confiscatePlayerItem', GetPlayerServerId(player), data.current.itemType, data.current.value, data.current.amount)
|
|
OpenBodySearchMenu(player)
|
|
end
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end, GetPlayerServerId(player))
|
|
end
|
|
|
|
self.OpenFineMenu = function(player)
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'fine', {
|
|
title = _U('job_police:fine'),
|
|
align = 'top-left',
|
|
elements = {
|
|
{label = _U('job_police:traffic_offense'), value = 0},
|
|
{label = _U('job_police:minor_offense'), value = 1},
|
|
{label = _U('job_police:average_offense'), value = 2},
|
|
{label = _U('job_police:major_offense'), value = 3}
|
|
}}, function(data, menu)
|
|
OpenFineCategoryMenu(player, data.current.value)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end
|
|
|
|
self.OpenFineCategoryMenu = function(player, category)
|
|
ESX.TriggerServerCallback('esx_policejob:getFineList', function(fines)
|
|
local elements = {}
|
|
|
|
for k,fine in ipairs(fines) do
|
|
table.insert(elements, {
|
|
label = ('%s <span style="color:green;">%s</span>'):format(fine.label, _U('job_police:armory_item', ESX.Math.GroupDigits(fine.amount))),
|
|
value = fine.id,
|
|
amount = fine.amount,
|
|
fineLabel = fine.label
|
|
})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'fine_category', {
|
|
title = _U('job_police:fine'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
menu.close()
|
|
|
|
if self.Config.EnablePlayerManagement then
|
|
TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', _U('job_police:fine_total', data.current.fineLabel), data.current.amount)
|
|
else
|
|
TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), '', _U('job_police:fine_total', data.current.fineLabel), data.current.amount)
|
|
end
|
|
|
|
ESX.SetTimeout(300, function()
|
|
OpenFineCategoryMenu(player, category)
|
|
end)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end, category)
|
|
end
|
|
|
|
self.LookupVehicle = function()
|
|
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'lookup_vehicle', {
|
|
title = _U('job_police:search_database_title'),
|
|
}, function(data, menu)
|
|
local length = string.len(data.value)
|
|
if not data.value or length < 2 or length > 8 then
|
|
ESX.ShowNotification(_U('job_police:search_database_error_invalid'))
|
|
else
|
|
ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo)
|
|
local elements = {{label = _U('job_police:plate', retrivedInfo.plate)}}
|
|
menu.close()
|
|
|
|
if not retrivedInfo.owner then
|
|
table.insert(elements, {label = _U('job_police:owner_unknown')})
|
|
else
|
|
table.insert(elements, {label = _U('job_police:owner', retrivedInfo.owner)})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_infos', {
|
|
title = _U('job_police:vehicle_info'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, nil, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
end, data.value)
|
|
|
|
end
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end
|
|
|
|
self.ShowPlayerLicense = function(player)
|
|
local elements = {}
|
|
|
|
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
|
|
table.insert(elements, {
|
|
label = playerData.licenses[i].label,
|
|
type = playerData.licenses[i].type
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'manage_license', {
|
|
title = _U('job_police:license_revoke'),
|
|
align = 'top-left',
|
|
elements = elements,
|
|
}, function(data, menu)
|
|
ESX.ShowNotification(_U('job_police:licence_you_revoked', data.current.label, playerData.name))
|
|
TriggerServerEvent('esx_policejob:message', GetPlayerServerId(player), _U('job_police:license_revoked', data.current.label))
|
|
|
|
TriggerServerEvent('esx_license:removeLicense', GetPlayerServerId(player), data.current.type)
|
|
|
|
ESX.SetTimeout(300, function()
|
|
ShowPlayerLicense(player)
|
|
end)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
|
|
end, GetPlayerServerId(player))
|
|
end
|
|
|
|
self.OpenUnpaidBillsMenu = function(player)
|
|
local elements = {}
|
|
|
|
ESX.TriggerServerCallback('esx_billing:getTargetBills', function(bills)
|
|
for k,bill in ipairs(bills) do
|
|
table.insert(elements, {
|
|
label = ('%s - <span style="color:red;">%s</span>'):format(bill.label, _U('job_police:armory_item', ESX.Math.GroupDigits(bill.amount))),
|
|
billId = bill.id
|
|
})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'billing', {
|
|
title = _U('job_police:unpaid_bills'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, nil, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end, GetPlayerServerId(player))
|
|
end
|
|
|
|
self.OpenVehicleInfosMenu = function(vehicleData)
|
|
ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo)
|
|
local elements = {{label = _U('job_police:plate', retrivedInfo.plate)}}
|
|
|
|
if not retrivedInfo.owner then
|
|
table.insert(elements, {label = _U('job_police:owner_unknown')})
|
|
else
|
|
table.insert(elements, {label = _U('job_police:owner', retrivedInfo.owner)})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_infos', {
|
|
title = _U('job_police:vehicle_info'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, nil, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end, vehicleData.plate)
|
|
end
|
|
|
|
self.OpenGetWeaponMenu = function()
|
|
ESX.TriggerServerCallback('esx_policejob:getArmoryWeapons', function(weapons)
|
|
local elements = {}
|
|
|
|
for i=1, #weapons, 1 do
|
|
if weapons[i].count > 0 then
|
|
table.insert(elements, {
|
|
label = 'x' .. weapons[i].count .. ' ' .. ESX.GetWeaponLabel(weapons[i].name),
|
|
value = weapons[i].name
|
|
})
|
|
end
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_get_weapon', {
|
|
title = _U('job_police:get_weapon_menu'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
menu.close()
|
|
|
|
ESX.TriggerServerCallback('esx_policejob:removeArmoryWeapon', function()
|
|
OpenGetWeaponMenu()
|
|
end, data.current.value)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end)
|
|
end
|
|
|
|
self.OpenPutWeaponMenu = function()
|
|
local elements = {}
|
|
local playerPed = PlayerPedId()
|
|
local weaponList = ESX.GetWeaponList()
|
|
|
|
for i=1, #weaponList, 1 do
|
|
local weaponHash = GetHashKey(weaponList[i].name)
|
|
|
|
if HasPedGotWeapon(playerPed, weaponHash, false) and weaponList[i].name ~= 'WEAPON_UNARMED' then
|
|
table.insert(elements, {
|
|
label = weaponList[i].label,
|
|
value = weaponList[i].name
|
|
})
|
|
end
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_put_weapon', {
|
|
title = _U('job_police:put_weapon_menu'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
menu.close()
|
|
|
|
ESX.TriggerServerCallback('esx_policejob:addArmoryWeapon', function()
|
|
OpenPutWeaponMenu()
|
|
end, data.current.value, true)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end
|
|
|
|
self.OpenBuyWeaponsMenu = function()
|
|
local elements = {}
|
|
local playerPed = PlayerPedId()
|
|
|
|
local authorizedWeapons = {}
|
|
|
|
for i=1, ESX.PlayerData.job.grade, 1 do
|
|
|
|
local weapons = self.Config.AuthorizedWeapons[i]
|
|
|
|
for j=1, #weapons, 1 do
|
|
authorizedWeapons[#authorizedWeapons + 1] = weapons[j]
|
|
end
|
|
|
|
end
|
|
|
|
for k,v in ipairs(authorizedWeapons) do
|
|
local weaponNum, weapon = ESX.GetWeapon(v.weapon)
|
|
local components, label = {}
|
|
local hasWeapon = HasPedGotWeapon(playerPed, GetHashKey(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, GetHashKey(v.weapon), component.hash)
|
|
|
|
if hasComponent then
|
|
label = ('%s: <span style="color:green;">%s</span>'):format(component.label, _U('job_police:armory_owned'))
|
|
else
|
|
if v.components[i] > 0 then
|
|
label = ('%s: <span style="color:green;">%s</span>'):format(component.label, _U('job_police:armory_item', ESX.Math.GroupDigits(v.components[i])))
|
|
else
|
|
label = ('%s: <span style="color:green;">%s</span>'):format(component.label, _U('job_police:armory_free'))
|
|
end
|
|
end
|
|
|
|
table.insert(components, {
|
|
label = 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: <span style="color:green;">></span>'):format(weapon.label)
|
|
elseif hasWeapon and not v.components then
|
|
label = ('%s: <span style="color:green;">%s</span>'):format(weapon.label, _U('job_police:armory_owned'))
|
|
else
|
|
if v.price > 0 then
|
|
label = ('%s: <span style="color:green;">%s</span>'):format(weapon.label, _U('job_police:armory_item', ESX.Math.GroupDigits(v.price)))
|
|
else
|
|
label = ('%s: <span style="color:green;">%s</span>'):format(weapon.label, _U('job_police:armory_free'))
|
|
end
|
|
end
|
|
|
|
table.insert(elements, {
|
|
label = label,
|
|
weaponLabel = weapon.label,
|
|
name = weapon.name,
|
|
components = components,
|
|
price = v.price,
|
|
hasWeapon = hasWeapon
|
|
})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_buy_weapons', {
|
|
title = _U('job_police:armory_weapontitle'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
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(_U('job_police:armory_bought', data.current.weaponLabel, ESX.Math.GroupDigits(data.current.price)))
|
|
end
|
|
|
|
menu.close()
|
|
OpenBuyWeaponsMenu()
|
|
else
|
|
ESX.ShowNotification(_U('job_police:armory_money'))
|
|
end
|
|
end, data.current.name, 1)
|
|
end
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end
|
|
|
|
self.OpenWeaponComponentShop = function(components, weaponName, parentShop)
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_buy_weapons_components', {
|
|
title = _U('job_police:armory_componenttitle'),
|
|
align = 'top-left',
|
|
elements = components
|
|
}, function(data, menu)
|
|
if data.current.hasComponent then
|
|
ESX.ShowNotification(_U('job_police:armory_hascomponent'))
|
|
else
|
|
ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought)
|
|
if bought then
|
|
if data.current.price > 0 then
|
|
ESX.ShowNotification(_U('job_police:armory_bought', data.current.componentLabel, ESX.Math.GroupDigits(data.current.price)))
|
|
end
|
|
|
|
menu.close()
|
|
parentShop.close()
|
|
OpenBuyWeaponsMenu()
|
|
else
|
|
ESX.ShowNotification(_U('job_police:armory_money'))
|
|
end
|
|
end, weaponName, 2, data.current.componentNum)
|
|
end
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end
|
|
|
|
self.OpenGetStocksMenu = function()
|
|
ESX.TriggerServerCallback('esx_policejob:getStockItems', function(items)
|
|
local elements = {}
|
|
|
|
for i=1, #items, 1 do
|
|
table.insert(elements, {
|
|
label = 'x' .. items[i].count .. ' ' .. items[i].label,
|
|
value = items[i].name
|
|
})
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'stocks_menu', {
|
|
title = _U('job_police:police_stock'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
local itemName = data.current.value
|
|
|
|
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'stocks_menu_get_item_count', {
|
|
title = _U('job_police:quantity')
|
|
}, function(data2, menu2)
|
|
local count = tonumber(data2.value)
|
|
|
|
if not count then
|
|
ESX.ShowNotification(_U('job_police:quantity_invalid'))
|
|
else
|
|
menu2.close()
|
|
menu.close()
|
|
TriggerServerEvent('esx_policejob:getStockItem', itemName, count)
|
|
|
|
Citizen.Wait(300)
|
|
OpenGetStocksMenu()
|
|
end
|
|
end, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end)
|
|
end
|
|
|
|
self.OpenPutStocksMenu = function()
|
|
ESX.TriggerServerCallback('esx_policejob:getPlayerInventory', function(inventory)
|
|
local elements = {}
|
|
|
|
for i=1, #inventory.items, 1 do
|
|
local item = inventory.items[i]
|
|
|
|
if item.count > 0 then
|
|
table.insert(elements, {
|
|
label = item.label .. ' x' .. item.count,
|
|
type = 'item_standard',
|
|
value = item.name
|
|
})
|
|
end
|
|
end
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'stocks_menu', {
|
|
title = _U('job_police:inventory'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
local itemName = data.current.value
|
|
|
|
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'stocks_menu_put_item_count', {
|
|
title = _U('job_police:quantity')
|
|
}, function(data2, menu2)
|
|
local count = tonumber(data2.value)
|
|
|
|
if not count then
|
|
ESX.ShowNotification(_U('job_police:quantity_invalid'))
|
|
else
|
|
menu2.close()
|
|
menu.close()
|
|
TriggerServerEvent('esx_policejob:putStockItems', itemName, count)
|
|
|
|
Citizen.Wait(300)
|
|
OpenPutStocksMenu()
|
|
end
|
|
end, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end)
|
|
end)
|
|
end
|
|
|
|
-- Create blip for colleagues
|
|
self.CreateBlip = function(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
|
|
end
|
|
|
|
-- handcuff timer, unrestrain the player after an certain amount of time
|
|
self.StartHandcuffTimer = function()
|
|
if self.Config.EnableHandcuffTimer and handcuffTimer.active then
|
|
ESX.ClearTimeout(handcuffTimer.task)
|
|
end
|
|
|
|
handcuffTimer.active = true
|
|
|
|
handcuffTimer.task = ESX.SetTimeout(Config.HandcuffTimer, function()
|
|
ESX.ShowNotification(_U('job_police: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
|
|
self.ImpoundVehicle = function(vehicle)
|
|
--local vehicleName = GetLabelText(GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)))
|
|
ESX.Game.DeleteVehicle(vehicle)
|
|
ESX.ShowNotification(_U('job_police:impound_successful'))
|
|
currentTask.busy = false
|
|
end
|
|
|
|
self.OpenVehicleSpawnerMenu = function(type, config)
|
|
|
|
local playerCoords = GetEntityCoords(PlayerPedId())
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle', {
|
|
title = _U('job_police:garage_title'),
|
|
align = 'top-left',
|
|
elements = {
|
|
{label = _U('job_police:garage_storeditem'), action = 'garage'},
|
|
{label = _U('job_police:garage_storeitem'), action = 'store_garage'},
|
|
{label = _U('job_police:garage_buyitem'), action = 'buy_vehicle'}
|
|
}}, function(data, menu)
|
|
|
|
if data.current.action == 'buy_vehicle' then
|
|
|
|
local shopElements = {}
|
|
local authorizedVehicles = {}
|
|
|
|
for i=1, ESX.PlayerData.job.grade, 1 do
|
|
|
|
local vehicles = self.Config.AuthorizedVehicles[type][i]
|
|
|
|
for j=1, #vehicles, 1 do
|
|
authorizedVehicles[#authorizedVehicles + 1] = vehicles[j]
|
|
end
|
|
|
|
end
|
|
|
|
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))
|
|
|
|
table.insert(shopElements, {
|
|
label = ('%s - <span style="color:green;">%s</span>'):format(vehicleLabel, _U('job_police: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
|
|
self.OpenShopMenu(shopElements, config)
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_notauthorized'))
|
|
end
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_notauthorized'))
|
|
end
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_notauthorized'))
|
|
end
|
|
elseif data.current.action == 'garage' then
|
|
local 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 - <span style="color:darkgoldenrod;">%s</span>: '):format(vehicleName, props.plate)
|
|
|
|
if v.stored then
|
|
label = label .. ('<span style="color:green;">%s</span>'):format(_U('job_police:garage_stored'))
|
|
else
|
|
label = label .. ('<span style="color:darkred;">%s</span>'):format(_U('job_police:garage_notstored'))
|
|
end
|
|
|
|
table.insert(garage, {
|
|
label = label,
|
|
stored = v.stored,
|
|
model = props.model,
|
|
plate = props.plate
|
|
})
|
|
|
|
allVehicleProps[props.plate] = props
|
|
end
|
|
end
|
|
|
|
if #garage > 0 then
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_garage', {
|
|
title = _U('job_police:garage_title'),
|
|
align = 'top-left',
|
|
elements = garage
|
|
}, function(data2, menu2)
|
|
if data2.current.stored then
|
|
|
|
|
|
local foundSpawn, spawnPoint = self.GetAvailableVehicleSpawnPoint(config.SpawnPoints)
|
|
|
|
if foundSpawn then
|
|
menu2.close()
|
|
|
|
ESX.Game.SpawnVehicle(data2.current.model, spawnPoint.coords, spawnPoint.heading, function(vehicle)
|
|
local vehicleProps = allVehicleProps[data2.current.plate]
|
|
ESX.Game.SetVehicleProperties(vehicle, vehicleProps)
|
|
|
|
TriggerServerEvent('esx_vehicleshop:setJobVehicleState', data2.current.plate, false)
|
|
ESX.ShowNotification(_U('job_police:garage_released'))
|
|
end)
|
|
end
|
|
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_notavailable'))
|
|
end
|
|
end, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_empty'))
|
|
end
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_empty'))
|
|
end
|
|
end, type)
|
|
|
|
elseif data.current.action == 'store_garage' then
|
|
StoreNearbyVehicle(playerCoords)
|
|
end
|
|
end, function(data, menu)
|
|
menu.close()
|
|
end, nil, function()
|
|
|
|
if type == 'car' then
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:garage_prompt'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenVehicleSpawnerMenu('car', data.config)
|
|
end
|
|
|
|
elseif type == 'helicopter' then
|
|
|
|
ESX.ShowHelpNotification(_U('job_police:helicopter_prompt'))
|
|
|
|
self.CurrentAction = function()
|
|
self.OpenVehicleSpawnerMenu('helicopter', data.config)
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
self.StoreNearbyVehicle = function(playerCoords)
|
|
local vehicles, vehiclePlates = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}
|
|
|
|
if #vehicles > 0 then
|
|
for k,v in ipairs(vehicles) do
|
|
|
|
-- Make sure the vehicle we're saving is empty, or else it wont be deleted
|
|
if GetVehicleNumberOfPassengers(v) == 0 and IsVehicleSeatFree(v, -1) then
|
|
table.insert(vehiclePlates, {
|
|
vehicle = v,
|
|
plate = ESX.Math.Trim(GetVehicleNumberPlateText(v))
|
|
})
|
|
end
|
|
end
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_store_nearby'))
|
|
return
|
|
end
|
|
|
|
ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(storeSuccess, foundNum)
|
|
if storeSuccess then
|
|
local vehicleId = vehiclePlates[foundNum]
|
|
local attempts = 0
|
|
ESX.Game.DeleteVehicle(vehicleId.vehicle)
|
|
IsBusy = true
|
|
|
|
Citizen.CreateThread(function()
|
|
BeginTextCommandBusyspinnerOn('STRING')
|
|
AddTextComponentSubstringPlayerName(_U('job_police:garage_storing'))
|
|
EndTextCommandBusyspinnerOn(4)
|
|
|
|
while IsBusy do
|
|
Citizen.Wait(100)
|
|
end
|
|
|
|
BusyspinnerOff()
|
|
end)
|
|
|
|
-- Workaround for vehicle not deleting when other players are near it.
|
|
while DoesEntityExist(vehicleId.vehicle) do
|
|
Citizen.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 k,v in ipairs(vehicles) do
|
|
if ESX.Math.Trim(GetVehicleNumberPlateText(v)) == vehicleId.plate then
|
|
ESX.Game.DeleteVehicle(v)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
IsBusy = false
|
|
ESX.ShowNotification(_U('job_police:garage_has_stored'))
|
|
else
|
|
ESX.ShowNotification(_U('job_police:garage_has_notstored'))
|
|
end
|
|
end, vehiclePlates)
|
|
end
|
|
|
|
self.GetAvailableVehicleSpawnPoint = function(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(_U('job_police:vehicle_blocked'))
|
|
return false
|
|
end
|
|
end
|
|
|
|
self.OpenShopMenu = function(elements, config)
|
|
|
|
local playerPed = PlayerPedId()
|
|
isInShopMenu = true
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop', {
|
|
title = _U('job_police:vehicleshop_title'),
|
|
align = 'top-left',
|
|
elements = elements
|
|
}, function(data, menu)
|
|
|
|
menu.close()
|
|
|
|
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop_confirm', {
|
|
title = _U('job_police:vehicleshop_confirm', data.current.name, data.current.price),
|
|
align = 'top-left',
|
|
elements = {
|
|
{label = _U('job_police:confirm_no'), value = 'no'},
|
|
{label = _U('job_police:confirm_yes'), value = 'yes'}
|
|
}}, function(data2, menu2)
|
|
|
|
menu2.close()
|
|
|
|
if data2.current.value == 'yes' then
|
|
|
|
ESX.TriggerServerCallback('esx_policejob:canBuyVehicle', function (success)
|
|
|
|
if success then
|
|
|
|
local foundSpawn, spawnPoint = self.GetAvailableVehicleSpawnPoint(config.SpawnPoints)
|
|
|
|
if foundSpawn then
|
|
|
|
ESX.Game.SpawnVehicle(data.current.model, spawnPoint.coords, spawnPoint.heading, function(vehicle)
|
|
|
|
local props = ESX.Game.GetVehicleProperties(vehicle)
|
|
props.plate = exports['esx_vehicleshop']:GeneratePlate() -- TODO Should be in core
|
|
|
|
SetVehicleNumberPlateText(vehicle, props.plate)
|
|
TaskWarpPedIntoVehicle(playerPed, vehicle, -1)
|
|
|
|
TriggerServerEvent('esx_policejob:buyJobVehicle', props, data.current.type)
|
|
|
|
ESX.ShowNotification(_U('job_police:vehicleshop_bought', data.current.name, ESX.Math.GroupDigits(data.current.price)))
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
else
|
|
ESX.ShowNotification(_U('job_police:vehicleshop_money'))
|
|
menu2.close()
|
|
end
|
|
|
|
end, data.current.model, data.current.type)
|
|
|
|
end
|
|
|
|
end, function(data2, menu2)
|
|
menu2.close()
|
|
end)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
self.DeleteSpawnedVehicles = function()
|
|
|
|
for i=1, #self.SpawnedVehicles, 1 do
|
|
ESX.Game.DeleteVehicle(self.SpawnedVehicles[i])
|
|
end
|
|
|
|
self.SpawnedVehicles = {}
|
|
|
|
end
|
|
|
|
self.WaitForVehicleToLoad = function(modelHash)
|
|
|
|
modelHash = (type(modelHash) == 'number' and modelHash or GetHashKey(modelHash))
|
|
|
|
if not HasModelLoaded(modelHash) then
|
|
RequestModel(modelHash)
|
|
|
|
BeginTextCommandBusyspinnerOn('STRING')
|
|
AddTextComponentSubstringPlayerName(_U('vehicleshop_awaiting_model'))
|
|
EndTextCommandBusyspinnerOn(4)
|
|
|
|
while not HasModelLoaded(modelHash) do
|
|
Citizen.Wait(0)
|
|
DisableAllControlActions(0)
|
|
end
|
|
|
|
BusyspinnerOff()
|
|
end
|
|
end
|