323 lines
10 KiB
Lua
323 lines
10 KiB
Lua
|
|
local sharedConfig = require 'config.shared'
|
||
|
|
|
||
|
|
local function checkIsSpikeObject(spikeStrips, fixedCoords, position, maxDistance)
|
||
|
|
if #spikeStrips == 0 then return end
|
||
|
|
for i = 1, #spikeStrips do
|
||
|
|
local coords = fixedCoords[spikeStrips[i]]
|
||
|
|
|
||
|
|
local distance = #(position - coords)
|
||
|
|
if distance < maxDistance then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function getClosestObject(objects, position, maxDistance, isFixed)
|
||
|
|
if #objects == 0 then return end
|
||
|
|
local minDistance, currentIndex
|
||
|
|
|
||
|
|
for i = 1, #objects do
|
||
|
|
local coords
|
||
|
|
if isFixed then
|
||
|
|
coords = GlobalState.fixedCoords[objects[i]]
|
||
|
|
else
|
||
|
|
local object = NetworkGetEntityFromNetworkId(objects[i])
|
||
|
|
coords = GetEntityCoords(object)
|
||
|
|
end
|
||
|
|
|
||
|
|
local distance = #(position - coords)
|
||
|
|
if distance < maxDistance then
|
||
|
|
if not minDistance or distance < minDistance then
|
||
|
|
minDistance = distance
|
||
|
|
currentIndex = i
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return currentIndex
|
||
|
|
end
|
||
|
|
|
||
|
|
---Spawn police object.
|
||
|
|
---@param item string name from `config/shared.lua`
|
||
|
|
RegisterNetEvent('police:client:spawnPObj', function(item)
|
||
|
|
if QBX.PlayerData.job.type ~= 'leo' or not QBX.PlayerData.job.onduty then return end
|
||
|
|
|
||
|
|
if cache.vehicle then return exports.qbx_core:Notify(locale('error.in_vehicle'), 'error') end
|
||
|
|
|
||
|
|
if lib.progressBar({
|
||
|
|
duration = 2500,
|
||
|
|
label = locale('progressbar.place_object'),
|
||
|
|
useWhileDead = false,
|
||
|
|
canCancel = true,
|
||
|
|
disable = {
|
||
|
|
car = true,
|
||
|
|
move = true,
|
||
|
|
combat = true,
|
||
|
|
mouse = false
|
||
|
|
},
|
||
|
|
anim = {
|
||
|
|
dict = 'anim@narcotics@trash',
|
||
|
|
clip = 'drop_front'
|
||
|
|
}
|
||
|
|
}) then
|
||
|
|
local objectConfig = sharedConfig.objects[item]
|
||
|
|
local forward = GetEntityForwardVector(cache.ped)
|
||
|
|
local spawnCoords = GetEntityCoords(cache.ped) + forward * 0.5
|
||
|
|
local netid, error = lib.callback.await('police:server:spawnObject', false,
|
||
|
|
objectConfig.model, spawnCoords, GetEntityHeading(cache.ped))
|
||
|
|
|
||
|
|
if not netid then return exports.qbx_core:Notify(locale(error), 'error') end
|
||
|
|
|
||
|
|
local object = NetworkGetEntityFromNetworkId(netid)
|
||
|
|
PlaceObjectOnGroundProperly(object)
|
||
|
|
FreezeEntityPosition(object, objectConfig.freeze)
|
||
|
|
else
|
||
|
|
exports.qbx_core:Notify(locale('error.canceled'), 'error')
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('police:client:deleteObject', function()
|
||
|
|
local objectId = getClosestObject(GlobalState.policeObjects, GetEntityCoords(cache.ped) , 5.0)
|
||
|
|
if not objectId then return end
|
||
|
|
if lib.progressBar({
|
||
|
|
duration = 2500,
|
||
|
|
label = locale('progressbar.remove_object'),
|
||
|
|
useWhileDead = false,
|
||
|
|
canCancel = true,
|
||
|
|
disable = {
|
||
|
|
car = true,
|
||
|
|
move = true,
|
||
|
|
combat = true,
|
||
|
|
mouse = false
|
||
|
|
},
|
||
|
|
anim = {
|
||
|
|
dict = 'weapons@first_person@aim_rng@generic@projectile@thermal_charge@',
|
||
|
|
clip = 'plant_floor'
|
||
|
|
}
|
||
|
|
}) then
|
||
|
|
TriggerServerEvent('police:server:despawnObject', objectId)
|
||
|
|
else
|
||
|
|
exports.qbx_core:Notify(locale('error.canceled'), 'error')
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
---Spawn a spike strip.
|
||
|
|
RegisterNetEvent('police:client:SpawnSpikeStrip', function()
|
||
|
|
if QBX.PlayerData.job.type ~= 'leo' or not QBX.PlayerData.job.onduty then return end
|
||
|
|
if #GlobalState.spikeStrips >= sharedConfig.maxSpikes then
|
||
|
|
return exports.qbx_core:Notify(locale('error.no_spikestripe'), 'error')
|
||
|
|
end
|
||
|
|
|
||
|
|
if cache.vehicle then return exports.qbx_core:Notify(locale('error.in_vehicle'), 'error') end
|
||
|
|
|
||
|
|
if lib.progressBar({
|
||
|
|
duration = 2500,
|
||
|
|
label = locale('progressbar.place_object'),
|
||
|
|
useWhileDead = false,
|
||
|
|
canCancel = true,
|
||
|
|
disable = {
|
||
|
|
car = true,
|
||
|
|
move = true,
|
||
|
|
combat = true,
|
||
|
|
mouse = false
|
||
|
|
},
|
||
|
|
anim = {
|
||
|
|
dict = 'amb@medic@standing@kneel@enter',
|
||
|
|
clip = 'enter'
|
||
|
|
}
|
||
|
|
}) then
|
||
|
|
local spawnCoords = GetOffsetFromEntityInWorldCoords(cache.ped, 0.0, 2.0, 0)
|
||
|
|
local netid, error = lib.callback.await('police:server:spawnSpikeStrip', false,
|
||
|
|
spawnCoords, GetEntityHeading(cache.ped))
|
||
|
|
|
||
|
|
if not netid then
|
||
|
|
return exports.qbx_core:Notify(locale(error), 'error')
|
||
|
|
end
|
||
|
|
|
||
|
|
lib.requestAnimDict('p_ld_stinger_s')
|
||
|
|
local spike = NetworkGetEntityFromNetworkId(netid)
|
||
|
|
PlayEntityAnim(spike, 'p_stinger_s_deploy', 'p_ld_stinger_s', 1000.0, false, false, false, 0.0, 0)
|
||
|
|
PlaceObjectOnGroundProperly(spike)
|
||
|
|
RemoveAnimDict('p_ld_stinger_s')
|
||
|
|
else
|
||
|
|
exports.qbx_core:Notify(locale('error.canceled'), 'error')
|
||
|
|
end
|
||
|
|
|
||
|
|
RemoveAnimDict('amb@medic@standing@kneel@enter')
|
||
|
|
end)
|
||
|
|
|
||
|
|
local WHEEL_NAMES = {
|
||
|
|
'wheel_lf',
|
||
|
|
'wheel_rf',
|
||
|
|
'wheel_lm',
|
||
|
|
'wheel_rm',
|
||
|
|
'wheel_lr',
|
||
|
|
'wheel_rr',
|
||
|
|
}
|
||
|
|
|
||
|
|
local isSpike
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
if LocalPlayer.state.isLoggedIn then
|
||
|
|
isSpike = checkIsSpikeObject(GlobalState.spikeStrips, GlobalState.fixedCoords, GetEntityCoords(cache.ped), 30)
|
||
|
|
end
|
||
|
|
Wait(500)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
local isWatchInVehicleBusy
|
||
|
|
local function burstTyreOnSpikeCollision(vehicle)
|
||
|
|
CreateThread(function ()
|
||
|
|
if isWatchInVehicleBusy then return end
|
||
|
|
isWatchInVehicleBusy = true
|
||
|
|
local wheels = {}
|
||
|
|
for i = 1, #WHEEL_NAMES do
|
||
|
|
local w = GetEntityBoneIndexByName(vehicle, WHEEL_NAMES[i])
|
||
|
|
if w ~= -1 then
|
||
|
|
wheels[#wheels + 1] = { wheel = w, index = i - 1 }
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
pcall(lib.waitFor(function() return cache.value end, nil, sharedConfig.timeout))
|
||
|
|
|
||
|
|
while cache.vehicle do
|
||
|
|
local spikeStrips = GlobalState.spikeStrips
|
||
|
|
local fixedCoords = GlobalState.fixedCoords
|
||
|
|
if isSpike then
|
||
|
|
for i = 1, #wheels do
|
||
|
|
if wheels[i].wheel then
|
||
|
|
local wheelPosition = GetWorldPositionOfEntityBone(cache.vehicle, wheels[i].wheel)
|
||
|
|
|
||
|
|
if checkIsSpikeObject(spikeStrips, fixedCoords, wheelPosition, 1.8) then
|
||
|
|
local index = wheels[i].index
|
||
|
|
if not IsVehicleTyreBurst(cache.vehicle, index, true)
|
||
|
|
or IsVehicleTyreBurst(cache.vehicle, index, false)
|
||
|
|
then
|
||
|
|
SetVehicleTyreBurst(cache.vehicle, index, false, 1000.0)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
Wait(0)
|
||
|
|
else
|
||
|
|
Wait(250)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
isWatchInVehicleBusy = nil
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function displayInfoCloseToSpike()
|
||
|
|
CreateThread(function ()
|
||
|
|
pcall(lib.waitFor(function() return cache.value and nil or false end, nil, sharedConfig.timeout))
|
||
|
|
|
||
|
|
while not cache.vehicle and LocalPlayer.state.isLoggedIn and QBX.PlayerData.job.type == 'leo' and QBX.PlayerData.job.onduty do
|
||
|
|
local isOpen, text = lib.isTextUIOpen()
|
||
|
|
|
||
|
|
if isSpike and checkIsSpikeObject(GlobalState.spikeStrips, GlobalState.fixedCoords, GetEntityCoords(cache.ped), 3) then
|
||
|
|
if not isOpen or text ~= locale('info.delete_spike') then
|
||
|
|
lib.showTextUI(locale('info.delete_spike'))
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if isOpen and text == locale('info.delete_spike') then
|
||
|
|
lib.hideTextUI()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Wait(500)
|
||
|
|
end
|
||
|
|
|
||
|
|
local isOpen, text = lib.isTextUIOpen()
|
||
|
|
if isOpen and text == locale('info.delete_spike') then
|
||
|
|
lib.hideTextUI()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
local keybind
|
||
|
|
|
||
|
|
local function onPressed()
|
||
|
|
if cache.vehicle then return end
|
||
|
|
keybind:disable(true)
|
||
|
|
local spike = getClosestObject(GlobalState.spikeStrips, GetEntityCoords(cache.ped), 4, true)
|
||
|
|
if spike ~= nil then
|
||
|
|
if lib.progressBar({
|
||
|
|
duration = 2500,
|
||
|
|
label = locale('progressbar.remove_object'),
|
||
|
|
useWhileDead = false,
|
||
|
|
canCancel = true,
|
||
|
|
disable = {
|
||
|
|
car = true,
|
||
|
|
move = true,
|
||
|
|
combat = true,
|
||
|
|
mouse = false
|
||
|
|
},
|
||
|
|
anim = {
|
||
|
|
dict = 'weapons@first_person@aim_rng@generic@projectile@thermal_charge@',
|
||
|
|
clip = 'plant_floor'
|
||
|
|
}
|
||
|
|
}) then
|
||
|
|
TriggerServerEvent('police:server:despawnSpikeStrip', spike)
|
||
|
|
lib.hideTextUI()
|
||
|
|
else
|
||
|
|
exports.qbx_core:Notify(locale('error.canceled'), 'error')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
keybind:disable(false)
|
||
|
|
end
|
||
|
|
|
||
|
|
keybind = lib.addKeybind({
|
||
|
|
name = 'despawnSpikeStrip',
|
||
|
|
description = locale('info.delete_spike'),
|
||
|
|
defaultKey = 'E',
|
||
|
|
secondaryMapper = 'PAD_DIGITALBUTTONANY',
|
||
|
|
secondaryKey = 'LRIGHT_INDEX',
|
||
|
|
onPressed = onPressed
|
||
|
|
})
|
||
|
|
|
||
|
|
local function toggleJobFunctions(isWorkingLeo)
|
||
|
|
if isWorkingLeo then
|
||
|
|
keybind:disable(false)
|
||
|
|
|
||
|
|
if not cache.vehicle then
|
||
|
|
displayInfoCloseToSpike()
|
||
|
|
end
|
||
|
|
else
|
||
|
|
keybind:disable(true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
RegisterNetEvent('QBCore:Client:OnJobUpdate', function(job)
|
||
|
|
toggleJobFunctions(LocalPlayer.state.isLoggedIn and job.type == 'leo' and job.onduty)
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('QBCore:Client:SetDuty', function(onDuty)
|
||
|
|
local job = QBX.PlayerData.job
|
||
|
|
toggleJobFunctions(LocalPlayer.state.isLoggedIn and job and job.type == 'leo' and onDuty)
|
||
|
|
end)
|
||
|
|
|
||
|
|
AddStateBagChangeHandler('isLoggedIn', ('player:%s'):format(cache.serverId), function(_, _, isLoggedIn)
|
||
|
|
local job = QBX.PlayerData.job
|
||
|
|
toggleJobFunctions(isLoggedIn and job and job.type == 'leo' and job.onduty)
|
||
|
|
|
||
|
|
if cache.vehicle then
|
||
|
|
burstTyreOnSpikeCollision(cache.vehicle)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
lib.onCache('vehicle', function(vehicle)
|
||
|
|
if vehicle then
|
||
|
|
burstTyreOnSpikeCollision(vehicle)
|
||
|
|
else
|
||
|
|
displayInfoCloseToSpike()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
AddEventHandler('onResourceStop', function (resource)
|
||
|
|
if resource ~= GetCurrentResourceName() then return end
|
||
|
|
local isOpen, text = lib.isTextUIOpen()
|
||
|
|
if isOpen and text == locale('info.delete_spike') then
|
||
|
|
lib.hideTextUI()
|
||
|
|
end
|
||
|
|
end)
|