292 lines
9.6 KiB
Lua
292 lines
9.6 KiB
Lua
|
|
local config = require 'config.client'
|
||
|
|
|
||
|
|
---client uses key fob to toggle the door locks
|
||
|
|
---@param vehicle number The entity number of the vehicle.
|
||
|
|
local function toggleLock(vehicle)
|
||
|
|
if not vehicle then return end
|
||
|
|
local vehicleConfig = GetVehicleConfig(vehicle)
|
||
|
|
if vehicleConfig.noLock or vehicleConfig.shared then return end
|
||
|
|
if GetIsVehicleAccessible(vehicle) then
|
||
|
|
|
||
|
|
lib.playAnim(cache.ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49)
|
||
|
|
|
||
|
|
--- if the statebag is out of sync, rely on it as the source of truth and sync the client to the statebag's value
|
||
|
|
local stateBagValue = Entity(vehicle).state.doorslockstate
|
||
|
|
if GetVehicleDoorLockStatus(vehicle) ~= stateBagValue then
|
||
|
|
SetVehicleDoorsLocked(vehicle, stateBagValue)
|
||
|
|
end
|
||
|
|
|
||
|
|
local lockstate = (GetVehicleDoorLockStatus(vehicle) % 2) + 1
|
||
|
|
|
||
|
|
TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), lockstate)
|
||
|
|
exports.qbx_core:Notify(locale(lockstate == 2 and 'notify.vehicle_locked' or 'notify.vehicle_unlocked'))
|
||
|
|
|
||
|
|
qbx.playAudio({ audioName = 'Remote_Control_Fob', audioRef = 'PI_Menu_Sounds', source = vehicle })
|
||
|
|
SetVehicleLights(vehicle, 2)
|
||
|
|
Wait(250)
|
||
|
|
SetVehicleLights(vehicle, 1)
|
||
|
|
Wait(200)
|
||
|
|
SetVehicleLights(vehicle, 0)
|
||
|
|
Wait(300)
|
||
|
|
ClearPedTasks(cache.ped)
|
||
|
|
else
|
||
|
|
exports.qbx_core:Notify(locale('notify.no_keys'), 'error')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
---if the player does not have ignition access to the vehicle:
|
||
|
|
---check whether to give keys if engine is on
|
||
|
|
---disable the engine and listen for search keys if applicable to the vehicle
|
||
|
|
local function onEnteringDriverSeat()
|
||
|
|
local vehicle = cache.vehicle
|
||
|
|
EngineBind:disable(false)
|
||
|
|
CreateThread(function()
|
||
|
|
while cache.seat == -1 do
|
||
|
|
if IsControlJustPressed(0, 23) then -- vehicle enter/leave input
|
||
|
|
EngineBind:disable(true)
|
||
|
|
end
|
||
|
|
Wait(0)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
local vehicleConfig = GetVehicleConfig(vehicle)
|
||
|
|
if vehicleConfig.shared then return end
|
||
|
|
|
||
|
|
local isVehicleAccessible = GetIsVehicleAccessible(vehicle)
|
||
|
|
if isVehicleAccessible then return end
|
||
|
|
|
||
|
|
local isVehicleRunning = GetIsVehicleEngineRunning(vehicle)
|
||
|
|
if config.getKeysWhenEngineIsRunning and isVehicleRunning then
|
||
|
|
lib.print.debug("giving keys because engine is running")
|
||
|
|
TriggerServerEvent('qbx_vehiclekeys:server:playerEnteredVehicleWithEngineOn', VehToNet(vehicle))
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
lib.print.debug("player does not have access to vehicle ignition. Disabling engine.")
|
||
|
|
SetVehicleNeedsToBeHotwired(vehicle, false)
|
||
|
|
CreateThread(function()
|
||
|
|
while not isVehicleAccessible and cache.seat == -1 do
|
||
|
|
SetVehicleEngineOn(vehicle, false, true, true)
|
||
|
|
DisableControlAction(0, 71, true)
|
||
|
|
Wait(0)
|
||
|
|
isVehicleAccessible = GetIsVehicleAccessible(vehicle)
|
||
|
|
end
|
||
|
|
if lib.progressActive() then
|
||
|
|
lib.cancelProgress()
|
||
|
|
end
|
||
|
|
DisableKeySearch()
|
||
|
|
end)
|
||
|
|
|
||
|
|
if vehicleConfig.findKeysChance ~= 0.0 then
|
||
|
|
EnableKeySearch()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
lib.onCache('seat', function(newSeat)
|
||
|
|
if newSeat ~= -1 then return end
|
||
|
|
Wait(0) -- needed to update cache.seat
|
||
|
|
onEnteringDriverSeat()
|
||
|
|
end)
|
||
|
|
|
||
|
|
lib.onCache('vehicle', function(vehicle)
|
||
|
|
if not vehicle then return end
|
||
|
|
SetVehicleKeepEngineOnWhenAbandoned(vehicle, config.keepEngineOnWhenAbandoned)
|
||
|
|
end)
|
||
|
|
|
||
|
|
-----------------------
|
||
|
|
------ Key Binds ------
|
||
|
|
-----------------------
|
||
|
|
|
||
|
|
local togglelocksBind
|
||
|
|
togglelocksBind = lib.addKeybind({
|
||
|
|
name = 'togglelocks',
|
||
|
|
description = locale('info.toggle_locks'),
|
||
|
|
defaultKey = 'L',
|
||
|
|
onPressed = function()
|
||
|
|
togglelocksBind:disable(true)
|
||
|
|
local vehicle = lib.getClosestVehicle(GetEntityCoords(cache.ped), config.vehicleMaximumLockingDistance, true)
|
||
|
|
toggleLock(vehicle)
|
||
|
|
Wait(1000)
|
||
|
|
togglelocksBind:disable(false)
|
||
|
|
end
|
||
|
|
})
|
||
|
|
|
||
|
|
local function toggleEngine(vehicle)
|
||
|
|
if not GetIsVehicleAccessible(vehicle) then return end
|
||
|
|
local engineOn = GetIsVehicleEngineRunning(vehicle)
|
||
|
|
|
||
|
|
local vehicleModel = GetEntityModel(vehicle)
|
||
|
|
local vehicleClass = GetVehicleClass(vehicle)
|
||
|
|
local anim = config.anims.toggleEngine.model[vehicleModel]
|
||
|
|
or config.anims.toggleEngine.class[vehicleClass]
|
||
|
|
or config.anims.toggleEngine.default
|
||
|
|
if next(anim) then
|
||
|
|
lib.playAnim(cache.ped, anim.dict, anim.clip, 8.0, 8.0,-1, 48, 0)
|
||
|
|
Wait(0)
|
||
|
|
CreateThread(function()
|
||
|
|
while IsEntityPlayingAnim(cache.ped, anim.dict, anim.clip, 3) do
|
||
|
|
if IsControlJustPressed(0, 23) then -- enter/exit vehicle input
|
||
|
|
ClearPedTasks(cache.ped)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
Wait(0)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
--- aethestic wait so that the engine toggles when the player appears to touch the button/key
|
||
|
|
Wait(anim.delay or 0)
|
||
|
|
end
|
||
|
|
|
||
|
|
if cache.vehicle then
|
||
|
|
SetVehicleEngineOn(vehicle, not engineOn, false, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
EngineBind = lib.addKeybind({
|
||
|
|
name = 'toggleengine',
|
||
|
|
description = locale('info.engine'),
|
||
|
|
defaultKey = config.keySearchBind,
|
||
|
|
onPressed = function()
|
||
|
|
if cache.seat == -1 then
|
||
|
|
EngineBind:disable(true)
|
||
|
|
toggleEngine(cache.vehicle)
|
||
|
|
Wait(1000)
|
||
|
|
EngineBind:disable(false)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
})
|
||
|
|
|
||
|
|
-----------------------
|
||
|
|
---- Client Events ----
|
||
|
|
-----------------------
|
||
|
|
|
||
|
|
local isTakingKeys = false
|
||
|
|
RegisterNetEvent('QBCore:Client:VehicleInfo', function(data)
|
||
|
|
if not LocalPlayer.state.isLoggedIn or data.event ~= 'Entering' then return end
|
||
|
|
local vehicleConfig = GetVehicleConfig(data.vehicle)
|
||
|
|
if vehicleConfig.noLock or isTakingKeys then return end
|
||
|
|
isTakingKeys = true
|
||
|
|
local driver = GetPedInVehicleSeat(data.vehicle, -1)
|
||
|
|
|
||
|
|
if driver ~= 0 and IsEntityDead(driver) and not (vehicleConfig.carjackingImmune or IsPedAPlayer(driver)) then
|
||
|
|
TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', data.netId, 1)
|
||
|
|
if lib.progressCircle({
|
||
|
|
duration = 2500,
|
||
|
|
label = locale('progress.takekeys'),
|
||
|
|
position = 'bottom',
|
||
|
|
useWhileDead = false,
|
||
|
|
canCancel = true,
|
||
|
|
disable = {
|
||
|
|
car = true,
|
||
|
|
},
|
||
|
|
}) then
|
||
|
|
TriggerServerEvent('qbx_vehiclekeys:server:tookKeys', VehToNet(data.vehicle))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
isTakingKeys = false
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('lockpicks:UseLockpick', function(isAdvanced)
|
||
|
|
local vehicle = cache.vehicle
|
||
|
|
if vehicle then
|
||
|
|
if GetKeySearchEnabled() then
|
||
|
|
DisableKeySearch()
|
||
|
|
Hotwire(vehicle, isAdvanced)
|
||
|
|
EnableKeySearch()
|
||
|
|
else
|
||
|
|
Hotwire(vehicle, isAdvanced)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
LockpickDoor(isAdvanced)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent('qbx_vehiclekeys:client:OnLostKeys', function()
|
||
|
|
if cache.seat == -1 then
|
||
|
|
onEnteringDriverSeat()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
for _, info in pairs(config.sharedKeys) do
|
||
|
|
if info.enableAutolock then
|
||
|
|
lib.onCache('vehicle', function (vehicle)
|
||
|
|
local leftVehicle = cache.vehicle
|
||
|
|
if not vehicle and leftVehicle then
|
||
|
|
local isShared = AreKeysJobShared(leftVehicle)
|
||
|
|
local isAutolockEnabled = config.sharedKeys[QBX.PlayerData.job.name]?.enableAutolock
|
||
|
|
|
||
|
|
if isShared and isAutolockEnabled then
|
||
|
|
TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(leftVehicle), 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
qbx.entityStateHandler('doorslockstate', function(entity, _, value)
|
||
|
|
if entity == 0 then return end
|
||
|
|
SetVehicleDoorsLocked(entity, value)
|
||
|
|
end)
|
||
|
|
|
||
|
|
AddEventHandler('onResourceStart', function(resourceName)
|
||
|
|
if (GetCurrentResourceName() ~= resourceName) then return end
|
||
|
|
if cache.seat == -1 then
|
||
|
|
onEnteringDriverSeat()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
---Checks the vehicle is always locked at spawn.
|
||
|
|
---@param vehicle number The entity number of the vehicle.
|
||
|
|
---@param isDriven boolean
|
||
|
|
---@return boolean `true` if the vehicle is locked, `false` otherwise.
|
||
|
|
local function getIsVehicleInitiallyLocked(vehicle, isDriven)
|
||
|
|
local vehicleConfig = GetVehicleConfig(vehicle)
|
||
|
|
local vehicleLockedChance = isDriven
|
||
|
|
and vehicleConfig.spawnLockedIfDriven
|
||
|
|
or vehicleConfig.spawnLockedIfParked
|
||
|
|
|
||
|
|
if type(vehicleLockedChance) == 'number' then
|
||
|
|
return math.random() < vehicleLockedChance
|
||
|
|
else
|
||
|
|
return vehicleLockedChance == true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function onVehicleAttemptToEnter(vehicle)
|
||
|
|
if Entity(vehicle).state.doorslockstate then return end
|
||
|
|
|
||
|
|
local ped = GetPedInVehicleSeat(vehicle, -1)
|
||
|
|
if IsPedAPlayer(ped) then return end
|
||
|
|
|
||
|
|
local isLocked = not GetVehicleConfig(vehicle).noLock and getIsVehicleInitiallyLocked(vehicle, ped and ped ~= 0)
|
||
|
|
local lockState = isLocked and 2 or 1
|
||
|
|
SetVehicleDoorsLocked(vehicle, lockState)
|
||
|
|
TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), lockState)
|
||
|
|
end
|
||
|
|
|
||
|
|
local isLoggedIn = false
|
||
|
|
|
||
|
|
local function playerEnterVehLoop()
|
||
|
|
CreateThread(function()
|
||
|
|
while isLoggedIn do
|
||
|
|
local vehicle = GetVehiclePedIsTryingToEnter(cache.ped)
|
||
|
|
if vehicle ~= 0 then
|
||
|
|
onVehicleAttemptToEnter(vehicle)
|
||
|
|
end
|
||
|
|
Wait(100)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
if LocalPlayer.state.isLoggedIn then
|
||
|
|
playerEnterVehLoop()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
AddStateBagChangeHandler('isLoggedIn', ('player:%s'):format(cache.serverId), function(_, _, value)
|
||
|
|
isLoggedIn = value
|
||
|
|
if not value then return end
|
||
|
|
playerEnterVehLoop()
|
||
|
|
end)
|