diff --git a/.vscode/settings.json b/.vscode/settings.json index 1f091d0..4e9be72 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "cache", "QBX", "locale", - "qbx" + "qbx", + "MySQL" ] } diff --git a/client/functions.lua b/client/functions.lua index 9cd107c..4c99900 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -5,7 +5,7 @@ local isCloseToCoords = functions.isCloseToCoords local alertSend = false local public = {} ---- Checks if the current player has a key for the specified vehicle. +---Checks if the current player has a key for the specified vehicle. ---@param vehicle number The entity number of the vehicle to check for a key. ---@return boolean? `true` if the player has a key for the vehicle, nil otherwise. function HasKey(vehicle) @@ -15,7 +15,7 @@ function HasKey(vehicle) return ent.state.keys[QBX.PlayerData.citizenid] end ---- Attempt to Give a key to a target player for the specified vehicle. +---Attempt to Give a key to a target player for the specified vehicle. ---@param targetPlayerId number The ID of the target player who will receive the key. ---@param vehicle number The entity number of the vehicle for which the key is being given. function GiveKey(targetPlayerId, vehicle) @@ -23,7 +23,7 @@ function GiveKey(targetPlayerId, vehicle) -- Will call the corresponding callback end ---- Attempt to Remove a key from a target player for the specified vehicle. +---Attempt to Remove a key from a target player for the specified vehicle. ---@param targetPlayerId number The ID of the target player from whom the key is being removed. ---@param vehicle number The entity number of the vehicle from which the key is being removed. function RemoveKey(targetPlayerId, vehicle) @@ -31,41 +31,47 @@ function RemoveKey(targetPlayerId, vehicle) -- Will call the corresponding callback end ---- Toggles the state of a vehicle's doors. If a door is open, it will be closed, and if it's closed, it will be opened. +---Toggles the state of a vehicle's doors. If a door is open, it will be closed, and if it's closed, it will be opened. ---@param vehicle number The entity number of the vehicle for which the door state is being toggled. function ToggleVehicleDoor(vehicle) -- This function is not yet implemented -- Will call the corresponding callback end ---- Checks if player has vehicle keys +---Checks if player has vehicle keys ---@param plate string The plate number of the vehicle. ---@return boolean? `true` if player has vehicle keys, `nil` otherwise. function public.hasKeys(plate) - local keysList = Player(cache.serverId).state.keysList or {} + local keysList = LocalPlayer.state.keysList or {} return keysList[plate] end + exports('HasKeys', public.hasKeys) ---- Checking weapon on the blacklist. ---- @return boolean? `true` if the vehicle is blacklisted, `nil` otherwise. +---Checking weapon on the blacklist. +---@return boolean? `true` if the vehicle is blacklisted, `nil` otherwise. function public.isBlacklistedWeapon() local weapon = GetSelectedPedWeapon(cache.ped) - - for _, w in ipairs(config.noCarjackWeapons) do - if weapon == joaat(w) then return true end + for i = 1, #config.noCarjackWeapons do + if weapon == joaat(config.noCarjackWeapons[i]) then + return true + end end end ---- Checking vehicle on the blacklist. ---- @param vehicle number The entity number of the vehicle. ---- @return boolean? `true` if the vehicle is blacklisted, `nil` otherwise. +---Checking vehicle on the blacklist. +---@param vehicle number The entity number of the vehicle. +---@return boolean? `true` if the vehicle is blacklisted, `nil` otherwise. function public.isBlacklistedVehicle(vehicle) - if Entity(vehicle).state.ignoreLocks or GetVehicleClass(vehicle) == 13 then return true end + if Entity(vehicle).state.ignoreLocks or GetVehicleClass(vehicle) == 13 then + return true + end local vehicleHash = GetEntityModel(vehicle) - for _, v in ipairs(config.noLockVehicles) do - if vehicleHash == joaat(v) then return true end + for i = 1, #config.noLockVehicles do + if vehicleHash == joaat(config.noLockVehicles[i]) then + return true + end end end @@ -88,10 +94,10 @@ function public.attemptPoliceAlert(type) end end ---- Gets bone coords ---- @param entity number The entity index. ---- @param boneName string The entity bone name. ---- @return vector3 `Bone coords` if exists, `entity coords` otherwise. +---Gets bone coords +---@param entity number The entity index. +---@param boneName string The entity bone name. +---@return vector3 `Bone coords` if exists, `entity coords` otherwise. local function getBoneCoords(entity, boneName) local boneIndex = GetEntityBoneIndexByName(entity, boneName) @@ -102,15 +108,15 @@ local function getBoneCoords(entity, boneName) end end ---- checks if any of the bones are close enough to the coords ---- @param coords vector3 ---- @param entity number ---- @param bones table ---- @param maxDistance number ---- @return boolean? `true` if bone exists, `nil` otherwise. +---Checks if any of the bones are close enough to the coords +---@param coords vector3 +---@param entity number +---@param bones table +---@param maxDistance number +---@return boolean? `true` if bone exists, `nil` otherwise. local function isCloseToAnyBone(coords, entity, bones, maxDistance) - for _, boneName in ipairs(bones) do - local boneCoords = getBoneCoords(entity, boneName) + for i = 1, #bones do + local boneCoords = getBoneCoords(entity, bones[i]) if isCloseToCoords(coords, boneCoords, maxDistance) then return true end @@ -119,26 +125,26 @@ end local doorBones = {'door_dside_f', 'door_dside_r', 'door_pside_f', 'door_pside_r'} ---- Checking whether the character is close enough to the vehicle driver door. ---- @param vehicle number The entity number of the vehicle. ---- @param maxDistance number The max distance to check. ---- @return boolean? `true` if the player ped is next to an open vehicle, `nil` otherwise. +---Checking whether the character is close enough to the vehicle driver door. +---@param vehicle number The entity number of the vehicle. +---@param maxDistance number The max distance to check. +---@return boolean? `true` if the player ped is next to an open vehicle, `nil` otherwise. local function isVehicleInRange(vehicle, maxDistance) local vehicles = GetGamePool('CVehicle') local pedCoords = GetEntityCoords(cache.ped) - - for _, v in ipairs(vehicles) do + for i = 1, #vehicles do + local v = vehicles[i] if not cache.vehicle or v ~= cache.vehicle then - if vehicle == v - and isCloseToAnyBone(pedCoords, vehicle, doorBones, maxDistance) - then return true end + if vehicle == v and isCloseToAnyBone(pedCoords, vehicle, doorBones, maxDistance) then + return true + end end end end ---- Will be execuded when the opening of the lock succeeds. ---- @param vehicle number The entity number of the vehicle. ---- @param plate string The plate number of the vehicle. +---Will be execuded when the opening of the lock succeeds. +---@param vehicle number The entity number of the vehicle. +---@param plate string The plate number of the vehicle. local function lockpickSuccessCallback(vehicle, plate) TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) @@ -151,12 +157,12 @@ local function lockpickSuccessCallback(vehicle, plate) end end ---- Operations done after the LockpickDoor quickevent done. ---- @param vehicle number The entity number of the vehicle. ---- @param plate string The plate number of the vehicle. ---- @param isAdvancedLockedpick boolean Determines whether an advanced lockpick was used. ---- @param maxDistance number The max distance to check. ---- @param isSuccess boolean? Determines whether the lock has been successfully opened. +---Operations done after the LockpickDoor quickevent done. +---@param vehicle number The entity number of the vehicle. +---@param plate string The plate number of the vehicle. +---@param isAdvancedLockedpick boolean Determines whether an advanced lockpick was used. +---@param maxDistance number The max distance to check. +---@param isSuccess boolean? Determines whether the lock has been successfully opened. local function lockpickCallback(vehicle, plate, isAdvancedLockedpick, maxDistance, isSuccess) if not isVehicleInRange(vehicle, maxDistance) then return end -- the action will be aborted if the opened vehicle is too far. if isSuccess then @@ -182,14 +188,15 @@ local function lockpickCallback(vehicle, plate, isAdvancedLockedpick, maxDistanc end local islockpickingProcessLocked = false -- lock flag ---- Lockpicking quickevent. ---- @param isAdvancedLockedpick boolean Determines whether an advanced lockpick was used ---- @param maxDistance number? The max distance to check. ---- @param customChallenge boolean? lockpick challenge + +---Lockpicking quickevent. +---@param isAdvancedLockedpick boolean Determines whether an advanced lockpick was used +---@param maxDistance number? The max distance to check. +---@param customChallenge boolean? lockpick challenge function public.lockpickDoor(isAdvancedLockedpick, maxDistance, customChallenge) maxDistance = maxDistance or 2 local pedCoords = GetEntityCoords(cache.ped) - local vehicle = lib.getClosestVehicle(pedCoords, 4, false) + local vehicle = lib.getClosestVehicle(pedCoords, maxDistance * 2, false) -- The difference between the door and the center of the vehicle if not vehicle then return end @@ -198,31 +205,41 @@ function public.lockpickDoor(isAdvancedLockedpick, maxDistance, customChallenge) --- player may attempt to open the lock if: if not plate - or not isDriverSeatFree -- no one in the driver's seat - or public.hasKeys(plate) -- player does not have keys to the vehicle - or Entity(vehicle).state.isOpen -- the lock is locked + or not isDriverSeatFree -- no one in the driver's seat + or public.hasKeys(plate) -- player does not have keys to the vehicle + or Entity(vehicle).state.isOpen -- the lock is locked or not isCloseToAnyBone(pedCoords, vehicle, doorBones, maxDistance) -- the player's ped is close enough to the driver's door - or GetVehicleDoorLockStatus(vehicle) < 2 -- the vehicle is locked + or GetVehicleDoorLockStatus(vehicle) < 2 -- the vehicle is locked then return end if islockpickingProcessLocked then return end -- start of the critical section - islockpickingProcessLocked = true -- one call per player at a time + + islockpickingProcessLocked = true -- one call per player at a time CreateThread(function() - --- lock opening animation + -- lock opening animation lib.requestAnimDict('veh@break_in@0h@p_m_one@') TaskPlayAnim(cache.ped, 'veh@break_in@0h@p_m_one@', "low_force_entry_ds", 3.0, 3.0, -1, 16, 0, false, false, false) - local isSuccess = customChallenge or - lib.skillCheck({ 'easy', 'easy', { areaSize = 60, speedMultiplier = 1 }, 'medium' }, - { '1', '2', '3', '4' }) - + local isSuccess = customChallenge or lib.skillCheck({ 'easy', 'easy', { areaSize = 60, speedMultiplier = 1 }, 'medium' }, { '1', '2', '3', '4' }) lockpickCallback(vehicle, plate, isAdvancedLockedpick, maxDistance, isSuccess) - Wait(config.lockpickCooldown) end) - islockpickingProcessLocked = false -- end of the critical section + islockpickingProcessLocked = false -- end of the critical section +end + +---Get a vehicle in the players scope by the plate +---@param plate string +---@return integer? +function public.getVehicleByPlate(plate) + local vehicles = GetGamePool('CVehicle') + for i = 1, #vehicles do + local vehicle = vehicles[i] + if qbx.getVehiclePlate(vehicle) == plate then + return vehicle + end + end end return public diff --git a/client/main.lua b/client/main.lua index 9e96ebc..dfe87aa 100644 --- a/client/main.lua +++ b/client/main.lua @@ -4,17 +4,13 @@ local config = require 'config.client' local functions = require 'client.functions' -local sharedFunction = require 'shared.functions' - --- #region spread imported functions local hasKeys = functions.hasKeys local lockpickDoor = functions.lockpickDoor local attemptPoliceAlert = functions.attemptPoliceAlert local isBlacklistedWeapon = functions.isBlacklistedWeapon local isBlacklistedVehicle = functions.isBlacklistedVehicle - --- #endregion +local getVehicleByPlate = functions.getVehicleByPlate ----------------------- ---- Variables ---- @@ -31,20 +27,17 @@ local canCarjack = true local function giveKeys(id, plate) local distance = #(GetEntityCoords(cache.ped) - GetEntityCoords(GetPlayerPed(GetPlayerFromServerId(id)))) - - if distance < 1.5 and distance > 0.0 then + if distance < 3 then TriggerServerEvent('qb-vehiclekeys:server:GiveVehicleKeys', id, plate) else - exports.qbx_core:Notify(locale("notify.not_near"), 'error') + exports.qbx_core:Notify(locale('notify.not_near'), 'error') end end local function getVehicleInDirection(coordFromOffset, coordToOffset) - local coordFrom = GetOffsetFromEntityInWorldCoords(cache.ped, coordFromOffset.x, coordFromOffset.y, coordFromOffset - .z) + local coordFrom = GetOffsetFromEntityInWorldCoords(cache.ped, coordFromOffset.x, coordFromOffset.y, coordFromOffset.z) local coordTo = GetOffsetFromEntityInWorldCoords(cache.ped, coordToOffset.x, coordToOffset.y, coordToOffset.z) - local rayHandle = CastRayPointToPoint(coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 10, - cache.ped, 0) + local rayHandle = CastRayPointToPoint(coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 10, cache.ped, 0) local _, _, _, _, vehicle = GetShapeTestResult(rayHandle) return vehicle end @@ -53,85 +46,90 @@ end -- Raycasts picture: https://i.imgur.com/FRED0kV.png local function getVehicle() local vehicle = cache.vehicle - - local RaycastOffsetTable = { - { ['fromOffset'] = vector3(0.0, 0.0, 0.0), ['toOffset'] = vector3(0.0, 20.0, -10.0) }, -- Waist to ground 45 degree angle - { ['fromOffset'] = vector3(0.0, 0.0, 0.7), ['toOffset'] = vector3(0.0, 10.0, -10.0) }, -- Head to ground 30 degree angle - { ['fromOffset'] = vector3(0.0, 0.0, 0.7), ['toOffset'] = vector3(0.0, 10.0, -20.0) }, -- Head to ground 15 degree angle + local raycastOffsetTable = { + { fromOffset = vec3(0.0, 0.0, 0.0), toOffset = vec3(0.0, 20.0, -10.0) }, -- Waist to ground 45 degree angle + { fromOffset = vec3(0.0, 0.0, 0.7), toOffset = vec3(0.0, 10.0, -10.0) }, -- Head to ground 30 degree angle + { fromOffset = vec3(0.0, 0.0, 0.7), toOffset = vec3(0.0, 10.0, -20.0) }, -- Head to ground 15 degree angle } local count = 0 - while not vehicle and count < #RaycastOffsetTable do - count = count + 1 - vehicle = getVehicleInDirection(RaycastOffsetTable[count]['fromOffset'], RaycastOffsetTable[count]['toOffset']) + while not vehicle and count < #raycastOffsetTable do + count += 1 + vehicle = getVehicleInDirection(raycastOffsetTable[count]['fromOffset'], raycastOffsetTable[count]['toOffset']) + end + + if not IsEntityAVehicle(vehicle) then + vehicle = nil end - if not IsEntityAVehicle(vehicle) then vehicle = nil end return vehicle end local function areKeysJobShared(veh) local vehName = GetDisplayNameFromVehicleModel(GetEntityModel(veh)) - local vehPlate = GetVehicleNumberPlateText(veh) + local vehPlate = qbx.getVehiclePlate(veh) for job, v in pairs(config.sharedKeys) do if job == QBX.PlayerData.job.name then if config.sharedKeys[job].requireOnduty and not QBX.PlayerData.job.onduty then return false end + for _, vehicle in ipairs(v.vehicles) do if string.upper(vehicle) == string.upper(vehName) then if not hasKeys(vehPlate) then - TriggerServerEvent("qb-vehiclekeys:server:AcquireVehicleKeys", vehPlate) + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', vehPlate) end + return true end end end end + return false end -local function setVehicleDoorLock(veh, state, anim) - if veh then - if not isBlacklistedVehicle(veh) then - if hasKeys(qbx.getVehiclePlate(veh)) or areKeysJobShared(veh) then - local vehLockStatus = GetVehicleDoorLockStatus(veh) +---manages the opening of locks +---@param vehicle number? The entity number of the vehicle. +---@param state boolean? State of the vehicle lock. +---@param anim any Aniation +local function setVehicleDoorLock(vehicle, state, anim) + if not vehicle then return end + if not isBlacklistedVehicle(vehicle) then + if hasKeys(qbx.getVehiclePlate(vehicle)) or areKeysJobShared(vehicle) then + + if anim then + lib.requestAnimDict('anim@mp_player_intmenu@key_fob@') + TaskPlayAnim(cache.ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) + end - if anim then - lib.requestAnimDict('anim@mp_player_intmenu@key_fob@') - TaskPlayAnim(cache.ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) - end + TriggerServerEvent('InteractSound_SV:PlayWithinDistance', 5, 'lock', 0.3) + NetworkRequestControlOfEntity(vehicle) - TriggerServerEvent("InteractSound_SV:PlayWithinDistance", 5, "lock", 0.3) - - NetworkRequestControlOfEntity(veh) - if state then - state = state == true and 2 or 1 - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), state) - exports.qbx_core:Notify(state == 2 and locale("notify.vehicle_locked") or locale("notify.vehicle_unlocked"), 'inform') - else - if vehLockStatus == 1 then - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 2) - exports.qbx_core:Notify(locale("notify.vehicle_locked"), 'inform') - else - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) - exports.qbx_core:Notify(locale("notify.vehicle_unlocked"), 'inform') - end - end - SetVehicleLights(veh, 2) - Wait(250) - SetVehicleLights(veh, 1) - Wait(200) - SetVehicleLights(veh, 0) - Wait(300) - ClearPedTasks(cache.ped) + local lockstate + if state ~= nil then + lockstate = state and 2 or 1 else - exports.qbx_core:Notify(locale("notify.no_keys"), 'error') + lockstate = (GetVehicleDoorLockStatus(vehicle) % 2) + 1 -- (1 % 2) + 1 -> 2 (2 % 2) + 1 -> 1 end + + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), lockstate) + exports.qbx_core:Notify(locale(lockstate == 2 and 'notify.vehicle_locked' or 'notify.vehicle_unlocked')) + + SetVehicleLights(vehicle, 2) + Wait(250) + SetVehicleLights(vehicle, 1) + Wait(200) + SetVehicleLights(vehicle, 0) + Wait(300) + ClearPedTasks(cache.ped) else - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + exports.qbx_core:Notify(locale('notify.no_keys'), 'error') end + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), 1) end end -exports("SetVehicleDoorLock", setVehicleDoorLock) + +exports('SetVehicleDoorLock', setVehicleDoorLock) local function getOtherPlayersInVehicle(vehicle) local otherPeds = {} @@ -166,7 +164,7 @@ local function hotwire(vehicle, plate) if lib.progressCircle({ duration = hotwireTime, - label = locale("progress.searching_keys"), + label = locale('progress.searching_keys'), position = 'bottom', useWhileDead = false, canCancel = true, @@ -180,18 +178,20 @@ local function hotwire(vehicle, plate) combat = true, } }) then - if (math.random() <= config.hotwireChance[GetVehicleClass(vehicle)]) then + if math.random() <= config.hotwireChance[GetVehicleClass(vehicle)] then TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) else TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) exports.qbx_core:Notify(locale("notify.failed_keys"), 'error') end + Wait(config.timeBetweenHotwires) end SetTimeout(10000, function() - attemptPoliceAlert("steal") + attemptPoliceAlert('steal') end) + isHotwiring = false end @@ -202,11 +202,13 @@ local function carjackVehicle(target) lib.requestAnimDict('mp_am_hold_up') local vehicle = GetVehiclePedIsUsing(target) local occupants = getPedsInVehicle(vehicle) - for _, occupant in ipairs(occupants) do + for p = 1, #occupants do + local occupant = occupants[p] CreateThread(function() - TaskPlayAnim(occupant, "mp_am_hold_up", "holdup_victim_20s", 8.0, -8.0, -1, 49, 0, false, false, false) + TaskPlayAnim(occupant, 'mp_am_hold_up', 'holdup_victim_20s', 8.0, -8.0, -1, 49, 0, false, false, false) PlayPain(occupant, 6, 0) end) + Wait(math.random(200, 500)) end @@ -223,7 +225,7 @@ local function carjackVehicle(target) if lib.progressCircle({ duration = config.carjackingTimeInMs, - label = locale("progress.attempting_carjack"), + label = locale('progress.attempting_carjack'), position = 'bottom', useWhileDead = false, canCancel = true, @@ -254,13 +256,13 @@ local function carjackVehicle(target) TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) else - exports.qbx_core:Notify(locale("notify.carjack_failed"), 'error') + exports.qbx_core:Notify(locale('notify.carjack_failed'), 'error') makePedFlee(target) TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) end isCarjacking = false Wait(2000) - attemptPoliceAlert("carjack") + attemptPoliceAlert('carjack') Wait(config.delayBetweenCarjackingsInMs) canCarjack = true end @@ -281,19 +283,18 @@ CreateThread(function() local sleep = 1000 if LocalPlayer.state.isLoggedIn then sleep = 100 - local entering = GetVehiclePedIsTryingToEnter(cache.ped) local carIsImmune = false if entering ~= 0 and not isBlacklistedVehicle(entering) then - sleep = 2000 + sleep = 500 local plate = qbx.getVehiclePlate(entering) - local driver = GetPedInVehicleSeat(entering, -1) - for _, vehicle in ipairs(config.immuneVehicles) do - if GetEntityModel(entering) == joaat(vehicle) then + for i = 1, #config.immuneVehicles do + if GetEntityModel(entering) == joaat(config.immuneVehicles[i]) then carIsImmune = true end end + -- Driven vehicle logic if driver ~= 0 and not IsPedAPlayer(driver) and not hasKeys(plate) and not carIsImmune then if IsEntityDead(driver) then @@ -303,7 +304,7 @@ CreateThread(function() TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 1) if lib.progressCircle({ duration = 2500, - label = locale("progress.takekeys"), + label = locale('progress.takekeys'), position = 'bottom', useWhileDead = false, canCancel = true, @@ -323,19 +324,16 @@ CreateThread(function() --Make passengers flee local pedsInVehicle = getPedsInVehicle(entering) - for _, pedInVehicle in ipairs(pedsInVehicle) do + for i = 1, #pedsInVehicle do + local pedInVehicle = pedsInVehicle[i] if pedInVehicle ~= GetPedInVehicleSeat(entering, -1) then makePedFlee(pedInVehicle) end end end -- Parked car logic - elseif driver == 0 and not Entity(entering).state.isOpen and not hasKeys(plate) and not isTakingKeys then - if config.lockNPCParkedCars then - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 2) - else - TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 1) - end + elseif driver == 0 and not Entity(entering).state.isOpen and not hasKeys(plate) and not isTakingKeys and not Entity(entering).state.vehicleid then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), config.lockNPCParkedCars and 2 or 1) end end @@ -343,14 +341,12 @@ CreateThread(function() if cache.vehicle and not isHotwiring then sleep = 1000 local plate = qbx.getVehiclePlate(cache.vehicle) - - if GetPedInVehicleSeat(cache.vehicle, -1) == cache.ped + if cache.seat == -1 and not hasKeys(plate) and not isBlacklistedVehicle(cache.vehicle) and not areKeysJobShared(cache.vehicle) then sleep = 0 - local vehiclePos = GetOffsetFromEntityInWorldCoords(cache.vehicle, 0.0, 1.0, 0.5) qbx.drawText3d({ text = locale('info.search_keys'), coords = vehiclePos }) SetVehicleEngineOn(cache.vehicle, false, false, true) @@ -363,15 +359,16 @@ CreateThread(function() if config.carjackEnable and canCarjack then local aiming, target = GetEntityPlayerIsFreeAimingAt(cache.playerId) - if aiming and (target ~= nil and target ~= 0) then + if aiming and target and target ~= 0 then if DoesEntityExist(target) and IsPedInAnyVehicle(target, false) and not IsEntityDead(target) and not IsPedAPlayer(target) then - local targetVehicle = GetVehiclePedIsIn(target, false) - for _, vehicle in ipairs(config.immuneVehicles) do - if GetEntityModel(targetVehicle) == joaat(vehicle) then + local targetveh = GetVehiclePedIsIn(target, false) + for i = 1, #config.immuneVehicles do + if GetEntityModel(targetveh) == joaat(config.immuneVehicles[i]) then carIsImmune = true end end - if GetPedInVehicleSeat(targetVehicle, -1) == target and not isBlacklistedWeapon() then + + if GetPedInVehicleSeat(targetveh, -1) == target and not isBlacklistedWeapon() then local pos = GetEntityCoords(cache.ped) local targetpos = GetEntityCoords(target) if #(pos - targetpos) < 5.0 and not carIsImmune then @@ -382,6 +379,7 @@ CreateThread(function() end end end + Wait(sleep) end end) @@ -390,14 +388,14 @@ end) ---- Client Events ---- ----------------------- -RegisterKeyMapping('togglelocks', locale("info.toggle_locks"), 'keyboard', 'L') +RegisterKeyMapping('togglelocks', locale('info.toggle_locks'), 'keyboard', 'L') RegisterCommand('togglelocks', function() setVehicleDoorLock(getVehicle(), nil, true) end, false) -RegisterKeyMapping('engine', locale("info.engine"), 'keyboard', 'G') +RegisterKeyMapping('engine', locale('info.engine'), 'keyboard', 'G') RegisterCommand('engine', function() - TriggerEvent("qb-vehiclekeys:client:ToggleEngine") + TriggerEvent('qb-vehiclekeys:client:ToggleEngine') end, false) RegisterNetEvent('qb-vehiclekeys:client:ToggleEngine', function() @@ -408,25 +406,24 @@ RegisterNetEvent('qb-vehiclekeys:client:ToggleEngine', function() end end) -RegisterNetEvent('qb-vehiclekeys:client:GiveKeys', function(id) - local targetVehicle = getVehicle() +RegisterNetEvent('qb-vehiclekeys:client:GiveKeys', function(id, plate) + local targetVehicle = plate and getVehicleByPlate(plate) or cache.vehicle or getVehicle() if targetVehicle then local targetPlate = qbx.getVehiclePlate(targetVehicle) + if not hasKeys(targetPlate) then + return exports.qbx_core:Notify(locale('notify.no_keys'), 'error') + end - if not hasKeys(targetPlate) then return exports.qbx_core:Notify(locale("notify.no_keys"), 'error') end - - if id and type(id) == "number" then -- Give keys to specific ID + if id and type(id) == 'number' then -- Give keys to specific ID giveKeys(id, targetPlate) else - if IsPedSittingInVehicle(cache.ped, targetVehicle) then -- Give keys to everyone in vehicle + if IsPedSittingInVehicle(cache.ped, targetVehicle) then -- Give keys to everyone in vehicle local otherOccupants = getOtherPlayersInVehicle(targetVehicle) - - for occupant in otherOccupants do - TriggerServerEvent('qb-vehiclekeys:server:GiveVehicleKeys', - GetPlayerServerId(NetworkGetPlayerIndexFromPed(occupant)), targetPlate) + for p = 1, #otherOccupants do + TriggerServerEvent('qb-vehiclekeys:server:GiveVehicleKeys', GetPlayerServerId(NetworkGetPlayerIndexFromPed(otherOccupants[p])), targetPlate) end - else -- Give keys to closest player + else -- Give keys to closest player local playerId = lib.getClosestPlayer(GetEntityCoords(cache.ped), 3, false) giveKeys(playerId, targetPlate) end @@ -438,13 +435,7 @@ RegisterNetEvent('lockpicks:UseLockpick', function(isAdvanced) lockpickDoor(isAdvanced) end) -AddEventHandler('onResourceStart', function(resourceName) - if resourceName == GetCurrentResourceName() then - TriggerServerEvent('qbx-vehiclekeys:server:setPlayerKeys') - end -end) - --- #region Backwards Compatibility ONLY -- Remove at some point -- +--#region Backwards Compatibility ONLY -- Remove at some point -- RegisterNetEvent('qb-vehiclekeys:client:AddKeys', function(plate) TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) if cache.vehicle and plate == qbx.getVehiclePlate(cache.vehicle) then @@ -455,4 +446,4 @@ end) RegisterNetEvent('vehiclekeys:client:SetOwner', function(plate) TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) end) --- #endregion Backwards Compatibility ONLY -- Remove at some point -- +--#endregion Backwards Compatibility ONLY -- Remove at some point -- diff --git a/config/server.lua b/config/server.lua new file mode 100644 index 0000000..9f65e71 --- /dev/null +++ b/config/server.lua @@ -0,0 +1,3 @@ +return { + runClearCronMinutes = 5, +} diff --git a/fxmanifest.lua b/fxmanifest.lua index 60ab220..2e6ef91 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -8,21 +8,24 @@ version '1.0.0' ox_lib 'locale' shared_scripts { - '@ox_lib/init.lua', - '@qbx_core/modules/lib.lua', - 'shared/*.lua' + '@ox_lib/init.lua', + '@qbx_core/modules/lib.lua', + 'shared/*.lua' } client_scripts { - '@qbx_core/modules/playerdata.lua', - 'client/*.lua' + '@qbx_core/modules/playerdata.lua', + 'client/*.lua' } -server_script 'server/*.lua' +server_scripts { + '@oxmysql/lib/MySQL.lua', + 'server/*.lua' +} files { - 'locales/*.json', - 'config/client.lua' + 'locales/*.json', + 'config/client.lua' } dependencies { @@ -30,5 +33,5 @@ dependencies { 'qbx_core' } -use_experimental_fxv2_oal 'yes' lua54 'yes' +use_experimental_fxv2_oal 'yes' diff --git a/locales/cs.json b/locales/cs.json index 0230098..5213389 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -8,6 +8,8 @@ "givekeys": "Předejte někomu klíče. Pokud nemáte průkaz totožnosti, dejte je nejbližší osobě nebo všem ve vozidle.", "givekeys_id": "ID", "givekeys_id_help": "ID hráče", + "givekeys_plate": "SPZ", + "givekeys_plate_help": "SPZ", "remove_keys": "Odebrat někomu klíče od vozidla.", "remove_keys_id": "id", "remove_keys_id_help": "ID hráče", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "Krádež auta se nezdařila!", - "failed_lockedpick": "Nedaří se vám najít klíče a jste zklamaní.", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Vyplňte ID hráče a SPZ", "gave_keys": "Předáš klíče.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Dostanete klíčky od vozidla!", "no_keys": "Od tohoto vozidla nemáte klíče.", "not_near": "V blízkosti není nikdo, komu by bylo možné předat klíče", "vehicle_locked": "Vozidlo je zamčené!", "vehicle_lockedpick": "Podařilo se ti otevřít zámek dveří!", - "vehicle_unlocked": "Vozidlo odemčeno!" + "vehicle_unlocked": "Vozidlo odemčeno!", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Pokus o krádež auta...", diff --git a/locales/de.json b/locales/de.json index 601a244..e1e6683 100644 --- a/locales/de.json +++ b/locales/de.json @@ -8,6 +8,8 @@ "givekeys": "Übergebe die Schlüssel an jemanden. Wenn keine Bürger-ID angegeben, geht er an die nächstgelegene Person oder an alle Personen im Fahrzeug.", "givekeys_id": "id", "givekeys_id_help": "Bürger-ID", + "givekeys_plate": "kennzeichen", + "givekeys_plate_help": "Kennzeichen", "remove_keys": "Jemandem die Schlüssel eines Fahrzeugs abnehmen.", "remove_keys_id": "id", "remove_keys_id_help": "Bürger-ID", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "Knacken des Autos ist fehlgeschlagen!", - "failed_lockedpick": "Du kannst keine Schlüssel finden und bist frustriert.", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Gebe die Bürger-ID und das Kennzeichen an.", "gave_keys": "Du gibst die Schlüssel ab.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Du erhälst die Schlüssel für das Fahrzeug!", "no_keys": "Du hast keine Schlüssel für das Fahrzeug!", "not_near": "Es ist niemand in der Nähe, der den Schlüssel bekommen könnte!", "vehicle_locked": "Fahrzeug verriegelt!", "vehicle_lockedpick": "Du hast es geschafft, das Türschloss zu knacken!", - "vehicle_unlocked": "Fahrzeug entriegelt." + "vehicle_unlocked": "Fahrzeug entriegelt.", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Versuchter Autodiebstahl...", diff --git a/locales/en.json b/locales/en.json index cb57068..d1fd7d3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -1,18 +1,23 @@ { "addcom": { - "addkeys": "Adds keys to a vehicle for someone.", + "addkeys": "addkeys", + "addkeys_help": "Adds keys to a vehicle for someone.", "addkeys_id": "id", "addkeys_id_help": "Player ID", "addkeys_plate": "plate", "addkeys_plate_help": "Plate", - "givekeys": "Hand over the keys to someone. If no ID, gives to closest person or everyone in the vehicle.", + "givekeys": "givekeys", + "givekeys_help": "Hand over the keys to someone. If no ID, gives to closest person or everyone in the vehicle.", "givekeys_id": "id", "givekeys_id_help": "Player ID", - "remove_keys": "Remove keys to a vehicle for someone.", - "remove_keys_id": "id", - "remove_keys_id_help": "Player ID", - "remove_keys_plate": "plate", - "remove_keys_plate_help": "Plate" + "givekeys_plate": "plate", + "givekeys_plate_help": "Plate", + "removekeys": "removekeys", + "removekeys_help": "Remove keys to a vehicle for someone.", + "removekeys_id": "id", + "removekeys_id_help": "Player ID", + "removekeys_plate": "plate", + "removekeys_plate_help": "Plate" }, "info": { "engine": "Toggle Engine", @@ -26,12 +31,17 @@ "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Fill out the player ID and Plate arguments", "gave_keys": "You hand over the keys.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "You get keys to the vehicle!", + "keys_removed": "You lost keys to the vehicle!", "no_keys": "You don't have keys to this vehicle.", "not_near": "There is nobody nearby to hand keys to", "vehicle_locked": "Vehicle locked!", "vehicle_lockedpick": "You managed to pick the door lock open!", - "vehicle_unlocked": "Vehicle unlocked!" + "vehicle_unlocked": "Vehicle unlocked!", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Attempting Carjacking...", diff --git a/locales/es.json b/locales/es.json index deaedfd..22622ee 100644 --- a/locales/es.json +++ b/locales/es.json @@ -8,6 +8,8 @@ "givekeys": "Entregar llaves a alguien. Si no hay ID, entregar a la persona más cercana o a todos en el vehículo.", "givekeys_id": "id", "givekeys_id_help": "ID de jugador", + "givekeys_plate": "placa", + "givekeys_plate_help": "Placa", "remove_keys": "Quitar llaves de un vehículo a alguien.", "remove_keys_id": "id", "remove_keys_id_help": "ID de jugador", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "¡Robo de carro falló!", - "failed_lockedpick": "No logras encontrar las llaves y te frustras", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Llena los argumentos de ID y placa del jugador", "gave_keys": "Has entregado las llaves", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Has recibido las llaves del vehículo", "no_keys": "No tienes las llaves de este vehículo", "not_near": "No hay nadie cerca a quién darle las llaves", "vehicle_locked": "Vehículo cerrado", "vehicle_lockedpick": "Lograste abrir la cerradura", - "vehicle_unlocked": "Vehículo abierto" + "vehicle_unlocked": "Vehículo abierto", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Intentando robar carro...", diff --git a/locales/et.json b/locales/et.json index 6e0623c..36d7770 100644 --- a/locales/et.json +++ b/locales/et.json @@ -8,6 +8,8 @@ "givekeys": "Andke võtmed kellelegi üle. Kui isikut tõendav dokument puudub, annab see lähimale inimesele või kõigile sõidukis viibijatele.", "givekeys_id": "id", "givekeys_id_help": "Mängija ID", + "givekeys_plate": "Numbrimärk", + "givekeys_plate_help": "Numbrimärk", "remove_keys": "Eemaldage kellegi jaoks sõiduki võtmed.", "remove_keys_id": "id", "remove_keys_id_help": "Mängija ID", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "Autovargamine ebaõnnestus!", - "failed_lockedpick": "Te ei leia võtmeid ja olete pettunud.", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Täitke mängija ID ja plaadi argumendid", "gave_keys": "Annad võtmed üle.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Saate auto võtmed!", "no_keys": "Teil pole selle sõiduki võtmeid.", "not_near": "Läheduses pole kedagi, kellele võtmed kätte anda", "vehicle_locked": "Sõiduk lukus!", "vehicle_lockedpick": "Sul õnnestus ukselukk lahti keerata!", - "vehicle_unlocked": "Sõiduk avatud!" + "vehicle_unlocked": "Sõiduk avatud!", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Autovarguse katse...", diff --git a/locales/fr.json b/locales/fr.json index 7dc0416..2f1ed36 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -8,6 +8,8 @@ "givekeys": "Donner les clés à quelqu'un. Si aucun ID, donne à la personne la plus proche ou à tout le monde dans le véhicule.", "givekeys_id": "id", "givekeys_id_help": "ID du joueur", + "givekeys_plate": "plaque", + "givekeys_plate_help": "Plaque", "remove_keys": "Retirer les clés à un véhicule pour quelqu'un.", "remove_keys_id": "id", "remove_keys_id_help": "ID du joueur", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "Le détournement de voiture a échoué", - "failed_lockedpick": "Vous n'avez pas réussi à ouvrir le véhicule et vous êtes frustré.", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Remplissez les arguments ID et plaque.", "gave_keys": "Vous donnez les clés.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Vous obtenez les clés du véhicule!", "no_keys": "Vous n'avez pas de clés de ce véhicule.", "not_near": "Il n'y a personne à proximité.", "vehicle_locked": "Véhicule verrouillé!", "vehicle_lockedpick": "Vous avez réussi à ouvrir le véhicule!", - "vehicle_unlocked": "Véhicule déverrouillé!" + "vehicle_unlocked": "Véhicule déverrouillé!", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Tentative de vol de carjack..", diff --git a/locales/nl.json b/locales/nl.json new file mode 100644 index 0000000..dffe925 --- /dev/null +++ b/locales/nl.json @@ -0,0 +1,47 @@ +{ + "addcom": { + "addkeys": "Voegt sleutels aan een auto toe voor iemand.", + "addkeys_id": "id", + "addkeys_id_help": "Speler ID", + "addkeys_plate": "plate", + "addkeys_plate_help": "Kenteken", + "givekeys": "Geef sleutels aan iemand. Als er geen ID wordt gegeven, worden de sleutels aan iemand dichtbij of iedereen in de auto gegeven.", + "givekeys_id": "id", + "givekeys_id_help": "Speler ID", + "givekeys_plate": "plate", + "givekeys_plate_help": "Kenteken", + "remove_keys": "Verwijder sleutels naar een auto voor iemand.", + "remove_keys_id": "id", + "remove_keys_id_help": "Speler ID", + "remove_keys_plate": "plate", + "remove_keys_plate_help": "Kenteken" + }, + "info": { + "engine": "Zet motor aan/uit", + "search_keys": "~g~[H]~w~ - Zoek Sleutels", + "toggle_locks": "Zet auto op slot of haal hem van het slot af", + "vehicle_theft": "Voertuigdiefstal bezig. Type: " + }, + "notify": { + "carjack_failed": "Voertuigdiefstal mislukt!", + "failed_lockedpick": "Het lockpicken mislukte.", + "failed_keys": "Je kon de sleutels niet vinden en raakt gefrustreerd.", + "fpid": "Fill out the player ID and Plate arguments", + "gave_keys": "Je hebt de sleutels overgedragen.", + "added_keys": "Je hebt een kopie van de sleutels voor het voertuig met kenteken %s gegeven aan speler %s!", + "keys_taken": "Je kreeg de sleutels van het voertuig!", + "no_keys": "Je hebt de sleutels van dit voertuig niet.", + "not_near": "Er is niemand in de buurt om sleutels aan te geven", + "vehicle_locked": "Voertuig op slot gezet!", + "vehicle_lockedpick": "Je hebt de deur open gebroken!", + "vehicle_unlocked": "Voertuig van het slot gehaald!", + "removed_keys": "De sleutels voor het voertuig met kenteken %s voor speler %s zijn afgenomen!", + "removed_keys_player": "De sleutels voor het voertuig met kenteken %s zijn van je afgenomen!", + "player_offline": "Deze speler is niet online!" + }, + "progress": { + "attempting_carjack": "Voertuig Stelen...", + "searching_keys": "Sleutels Zoeken...", + "takekeys": "Sleutels van lichaam af halen..." + } +} diff --git a/locales/pt.json b/locales/pt.json index d3eb396..aed7975 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -8,6 +8,8 @@ "givekeys": "Entrega as chaves a alguém. Se não houver ID, dá à pessoa mais próxima ou a todos no veículo.", "givekeys_id": "id", "givekeys_id_help": "ID do Jogador", + "givekeys_plate": "placa", + "givekeys_plate_help": "Placa", "remove_keys": "Remove chaves de um veículo para alguém.", "remove_keys_id": "id", "remove_keys_id_help": "ID do Jogador", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "Falha no assalto ao veículo!", - "failed_lockedpick": "Falhas em encontrar as chaves e ficas frustrado.", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Preenche os argumentos do ID do jogador e da placa", "gave_keys": "Entregaste as chaves.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Recebeste as chaves do veículo!", "no_keys": "Não tens as chaves deste veículo.", "not_near": "Não há ninguém por perto para entregar as chaves", "vehicle_locked": "Veículo trancado!", "vehicle_lockedpick": "Conseguiste abrir a fechadura da porta!", - "vehicle_unlocked": "Veículo destrancado!" + "vehicle_unlocked": "Veículo destrancado!", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Tentativa de assalto ao veículo...", diff --git a/locales/ro.json b/locales/ro.json index c3c5180..cc6de43 100644 --- a/locales/ro.json +++ b/locales/ro.json @@ -8,6 +8,8 @@ "givekeys": "Dai cheile vehiculului unei alte persoane. Daca nu se precizeaza ID-ul, cheile se duc celei mai apropiate persoane de tine.", "givekeys_id": "ID", "givekeys_id_help": "ID-ul jucatorului caruia vrei sa-i dai cheile", + "givekeys_plate": "numar", + "givekeys_plate_help": "Numarul de inmatriculare al vehiculului", "remove_keys": "Anulezi setul de chei aditional, pentru o persoana.", "remove_keys_id": "ID", "remove_keys_id_help": "ID-ul jucatorului", @@ -22,15 +24,20 @@ }, "notify": { "carjack_failed": "Furtul de mașină a eșuat", - "failed_lockedpick": "Nu ai putut clona cheile masinii si te stresezi.", + "failed_lockedpick": "You failed to lockpick.", + "failed_keys": "You fail to find the keys and get frustrated.", "fpid": "Trebuie sa introduci ID-ul jucatorului si numarul de inmatriculare", "gave_keys": "Ai dat cheile vehiculului.", + "added_keys": "You gave a copy of the keys to vehicle %s to player %s!", "keys_taken": "Ai primit cheile vehiculului!", "no_keys": "Nu ai cheile acestui vehicul.", "not_near": "Nu este nimeni langa tine sa-i dai cheile vehiculului", "vehicle_locked": "Vehicul blocat - Alarma activa!", "vehicle_lockedpick": "Felicitari, ai reusit sa spargi incuietoarea!", - "vehicle_unlocked": "Vehicul deblocat - Alarma inactiva!" + "vehicle_unlocked": "Vehicul deblocat - Alarma inactiva!", + "removed_keys": "The keys to plate %s for player %s have been removed!", + "removed_keys_player": "Your keys to plate %s have been removed!", + "player_offline": "This player is not online!" }, "progress": { "attempting_carjack": "Incerci sa fur vehiculul...", @@ -38,3 +45,4 @@ "takekeys": "Iei chile de la vehicul..." } } + diff --git a/server/commands.lua b/server/commands.lua index ad1bfab..fda314d 100644 --- a/server/commands.lua +++ b/server/commands.lua @@ -1,64 +1,115 @@ -lib.addCommand('givekeys', { - help = locale("addcom.givekeys"), +----------------------- +---- Imports ---- +----------------------- + +local functions = require 'server.functions' + +local removeKeys = functions.removeKeys +local giveKeys = functions.giveKeys + +----------------------- +---- Commands ---- +----------------------- + +lib.addCommand(locale('addcom.givekeys'), { + help = locale('addcom.givekeys_help'), params = { { - name = locale("addcom.givekeys_id"), - type = 'number', - help = locale("addcom.givekeys_id_help"), + name = locale('addcom.givekeys_id'), + type = 'playerId', + help = locale('addcom.givekeys_id_help'), + }, + { + name = locale('addcom.givekeys_plate'), + type = 'string', + help = locale('addcom.addkeys_plate_help'), optional = true }, }, restricted = false, }, function (source, args) - TriggerClientEvent('qb-vehiclekeys:client:GiveKeys', source, args.id) + local id = args[locale('addcom.givekeys_id')] + if id and not exports.qbx_core:GetPlayer(id) then + return exports.qbx_core:Notify(source, locale('notify.player_offline'), 'error') + end + + TriggerClientEvent('qb-vehiclekeys:client:GiveKeys', source, id, args[locale('addcom.givekeys_plate')]) end) -lib.addCommand('addkeys', { - help = locale("addcom.addkeys"), +--- Gets the plate of the vehicle in which the executor is, if args.plate is nil +---@param source number ID of the player +---@param plate string? The plate number of the vehicle. +---@return string? +local function getPlayersVehiclePlate(source, plate) + if not plate then + local ped = GetPlayerPed(source) + local vehicle = GetVehiclePedIsIn(ped, false) + + if vehicle == 0 then return end + plate = qbx.getVehiclePlate(vehicle) + end + + return plate +end + +lib.addCommand(locale('addcom.addkeys'), { + help = locale('addcom.addkeys_help'), params = { { - name = 'id', - type = 'number', - help = locale("addcom.addkeys_id_help"), - optional = true + name = locale('addcom.addkeys_id'), + type = 'playerId', + help = locale('addcom.addkeys_id_help'), }, { - name = 'plate', + name = locale('addcom.addkeys_plate'), type = 'string', - help = locale("addcom.addkeys_plate_help"), + help = locale('addcom.addkeys_plate_help'), optional = true }, }, restricted = 'group.admin', }, function (source, args) - if not args.id or not args.plate then - return exports.qbx_core:Notify(source, locale("notify.fpid")) + local playerId = args[locale('addcom.addkeys_id')] + local plate = getPlayersVehiclePlate(source, args[locale('addcom.addkeys_plate')]) + + if not playerId or not plate then + return exports.qbx_core:Notify(source, locale('notify.fpid'), 'error') + end + + if giveKeys(playerId, plate) then + return exports.qbx_core:Notify(source, locale('notify.added_keys', plate, playerId), 'success') end - GiveKeys(args.id, args.plate) + exports.qbx_core:Notify(source, locale('notify.player_offline'), 'error') end) -lib.addCommand('removekeys', { - help = locale("addcom.remove_keys"), +lib.addCommand(locale('addcom.removekeys'), { + help = locale('addcom.removekeys_help'), params = { { - name = 'id', - type = 'number', - help = locale("addcom.remove_keys_id_help"), - optional = true + name = locale('addcom.removekeys_id'), + type = 'playerId', + help = locale('addcom.removekeys_id_help'), }, { - name = 'plate', + name = locale('addcom.removekeys_plate'), type = 'string', - help = locale("addcom.remove_keys_plate_help"), + help = locale('addcom.removekeys_plate_help'), optional = true } }, restricted = 'group.admin', }, function (source, args) - if not args.id or not args.plate then - return exports.qbx_core:Notify(source, locale("notify.fpid")) + local playerId = args[locale('addcom.removekeys_id')] + local plate = getPlayersVehiclePlate(source, args[locale('addcom.removekeys_plate')]) + + if not playerId or not plate then + return exports.qbx_core:Notify(source, locale('notify.fpid'), 'error') + end + + if removeKeys(playerId, plate) then + return exports.qbx_core:Notify(source, locale('notify.removed_keys', plate, playerId), 'success') end - RemoveKeys(args.id, args.plate) + exports.qbx_core:Notify(source, locale('notify.player_offline'), 'error') end) diff --git a/server/functions.lua b/server/functions.lua index fe00663..a60860e 100644 --- a/server/functions.lua +++ b/server/functions.lua @@ -1,4 +1,4 @@ ---- Checks for the existence of a key. +---Checks for the existence of a key. ---@param entity number The entity (vehicle) where we check for the existence of a key. ---@param citizenid string The CitizenID of the player whose key we check for. ---@return boolean? `true` if the player has a key for the vehicle, nil otherwise. @@ -9,7 +9,7 @@ function HasKey(entity, citizenid) return ent.state.keys[citizenid] end ---- Adds a key to the selected vehicle entity and returns a success status. +---Adds a key to the selected vehicle entity and returns a success status. ---@param entity number The entity (vehicle) to which the key is added. ---@param citizenid string The CitizenID of the player whose key is being added. ---@param doorState number | nil -- Sets the doorState of the vehicle if present @@ -33,7 +33,7 @@ function GiveKey(entity, citizenid, doorState) end end ---- Removes a key from the selected vehicle entity and returns a success status. +---Removes a key from the selected vehicle entity and returns a success status. ---@param entity number The entity (vehicle) from which the key is removed. ---@param citizenid string The CitizenID of the player whose key is being removed. ---@return boolean? `true` if the key was successfully removed, `nil` otherwise. @@ -53,7 +53,7 @@ function RemoveKey(entity, citizenid) end end ---- Sets the door state of the vehicle. +---Sets the door state of the vehicle. ---@param entity number The entity (vehicle) for which the door state is updated. ---@param doorState number The door state number to update. ---@return boolean? `true` if the door state was successfully updated, `nil` otherwise. @@ -67,7 +67,7 @@ function SetDoorState(entity, doorState) return true end ---- Toggles the door state of the vehicle between open and closed. +---Toggles the door state of the vehicle between open and closed. ---@param entity number The entity (vehicle) for which the door state is being toggled. ---@return number | nil returns the new doorState of the vehicle function ToggleDoorState(entity) @@ -83,3 +83,139 @@ function ToggleDoorState(entity) return 0 end end + +local public = {} +local config = require 'config.server' +local debug = GetConvarInt(('%s-debug'):format(GetCurrentResourceName()), 0) == 1 + +local keysList = {} ---holds key status for some time after player logs out (Prevents frustration by crashing the client) +local keysLifetime = {} ---Life timestamp of the keys of a character who has logged out + +---Removes old keys from server memory +lib.cron.new('*/'..config.runClearCronMinutes ..' * * * *', function () + local time = os.time() + local seconds = config.runClearCronMinutes * 60 + for citizenId, lifetime in pairs(keysLifetime) do + if lifetime + seconds < time then + keysList[citizenId] = nil + keysLifetime[citizenId] = nil + end + end +end, {debug = debug}) + +---Gets Citizen Id based on source +---@param source number ID of the player +---@return string? citizenid The player CitizenID, nil otherwise. +local function getCitizenId(source) + local player = exports.qbx_core:GetPlayer(source) + if not player then return end + + return player.PlayerData.citizenid +end + +---Looking for a vehicle in the world +---@param plate string The plate number of the vehicle. +---@return number? vehicle The entity number of the found vehicle, nil otherwise. +function public.findVehicleByPlate(plate) + local vehicles = GetAllVehicles() + for i = 1, #vehicles do + local vehicle = vehicles[i] + if qbx.getVehiclePlate(vehicle) == plate then + return vehicle + end + end +end + +---Loads a players vehicles to the vehicleList +---@param src integer +function public.addPlayer(src) + local citizenid = getCitizenId(src) + if not citizenid then return end + + local vehicles = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', { citizenid }) + + local state = {} + local platesAssociations = {} + + for i = 1, #vehicles do + platesAssociations[vehicles[i].plate] = true + end + + if keysList[citizenid] then + keysLifetime[citizenid] = nil + + for plate in pairs(keysList[citizenid]) do + platesAssociations[plate] = true + end + end + + local worldVehicles = GetAllVehicles() + for i = 1, #worldVehicles do + local vehiclePlate = qbx.getVehiclePlate(worldVehicles[i]) + if platesAssociations[vehiclePlate] then + state[vehiclePlate] = true + end + end + + Player(src).state:set('keysList', state, true) +end + +---Removes a players vehicles from the vehicleList +---@param src integer +function public.removePlayer(src) + local citizenid = getCitizenId(src) + if not citizenid then return end + + keysList[citizenid] = Player(src).state.keysList + keysLifetime[citizenid] = os.time() + + Player(src).state:set('keysList', nil, true) +end + +--- Removing the vehicle keys from the user +---@param source number ID of the player +---@param plate string The plate number of the vehicle. +function public.removeKeys(source, plate) + local citizenid = getCitizenId(source) + + if not citizenid then return end + + local keys = Player(source).state.keysList or {} + + if not keys[plate] then return end + keys[plate] = nil + + Player(source).state:set('keysList', keys, true) + + exports.qbx_core:Notify(source, locale('notify.keys_removed')) + + return true +end + +function public.hasKeys(source, plate) + return Player(source).state.keysList[plate] +end + +---Gives the user the keys to the vehicle +---@param source number ID of the player +---@param plate string The plate number of the vehicle. +function public.giveKeys(source, plate) + local citizenid = getCitizenId(source) + + if not citizenid then return end + + local keys = Player(source).state.keysList or {} + + if keys[plate] then return end + keys[plate] = true + + Player(source).state:set('keysList', keys, true) + + exports.qbx_core:Notify(source, locale('notify.keys_taken')) + + return true +end + +exports('GiveKeys', public.giveKeys) + +return public diff --git a/server/main.lua b/server/main.lua index 4841e91..6084731 100644 --- a/server/main.lua +++ b/server/main.lua @@ -1,11 +1,16 @@ ----------------------- ----- Variables ---- +---- Imports ---- ----------------------- -local keysList = {} +local functions = require 'server.functions' + +local hasKeys = functions.hasKeys +local giveKeys = functions.giveKeys +local addPlayer = functions.addPlayer +local removePlayer = functions.removePlayer ----------------------- ----- Server Events ---- +---- Events ---- ----------------------- -- Event to give keys. receiver can either be a single id, or a table of ids. @@ -13,26 +18,27 @@ local keysList = {} RegisterNetEvent('qb-vehiclekeys:server:GiveVehicleKeys', function(receiver, plate) local giver = source - if HasKeys(giver, plate) then - exports.qbx_core:Notify(giver, locale("notify.gave_keys")) - if type(receiver) == 'table' then - for r in receiver do - GiveKeys(receiver[r], plate) - end - else - GiveKeys(receiver, plate) + if not hasKeys(giver, plate) then + return exports.qbx_core:Notify(giver, locale('notify.no_keys')) + end + + if type(receiver) == 'table' then + for i = 1, receiver do + giveKeys(receiver[i], plate) end else - exports.qbx_core:Notify(giver, locale("notify.no_keys")) + giveKeys(receiver, plate) end + + exports.qbx_core:Notify(giver, locale('notify.gave_keys')) end) RegisterNetEvent('qb-vehiclekeys:server:AcquireVehicleKeys', function(plate) - GiveKeys(source, plate) + giveKeys(source, plate) end) RegisterNetEvent('qb-vehiclekeys:server:breakLockpick', function(itemName) - if not (itemName == "lockpick" or itemName == "advancedlockpick") then return end + if not (itemName == 'lockpick' or itemName == 'advancedlockpick') then return end exports.ox_inventory:RemoveItem(source, itemName, 1) end) @@ -40,60 +46,7 @@ RegisterNetEvent('qb-vehiclekeys:server:setVehLockState', function(vehNetId, sta SetVehicleDoorsLocked(NetworkGetEntityFromNetworkId(vehNetId), state) end) -local function getCitizenId(source) - local player = exports.qbx_core:GetPlayer(source) - - if not player then return end - local citizenid = player.PlayerData.citizenid - - return citizenid -end - -RegisterNetEvent('qbx-vehiclekeys:server:setPlayerKeys', function() - local src = source - local citizenid = getCitizenId(src) - Player(src).state.keysList = keysList[citizenid] -end) - ------------------------ ----- Functions ---- ------------------------ - -function GiveKeys(source, plate) - local keys = Player(source).state.keysList or {} - - if keys[plate] then return end - keys[plate] = true - Player(source).state:set('keysList', keys, true) - local citizenid = getCitizenId(source) - - if not citizenid then return end - - if not keysList[citizenid] then - keysList[citizenid] = {} - end - - keysList[citizenid][plate] = true - - exports.qbx_core:Notify(source, locale('notify.keys_taken')) -end - -exports('GiveKeys', GiveKeys) - -function RemoveKeys(source, plate) - local state = Player(source).state - - if not state.keysList[plate] then return end - local citizenid = getCitizenId(source) - state.keysList = keysList[citizenid] - keysList[citizenid][plate] = nil -end - -function HasKeys(source, plate) - return Player(source).state.keysList[plate] -end - ---- Gives a key to an entity based on the player's CitizenID. +---Gives a key to an entity based on the player's CitizenID. ---@param id integer The player's ID. ---@param netId number The network ID of the entity. ---@param doorState number | nil Sets the door state if given @@ -104,9 +57,8 @@ RegisterNetEvent('qb-vehiclekeys:server:GiveKey', function(id, netId, doorState) -- drop player end end) -exports('GiveKey', GiveKey) ---- Removes a key from an entity based on the player's CitizenID. +---Removes a key from an entity based on the player's CitizenID. ---@param id integer The player's ID. ---@param netId number The network ID of the entity. RegisterNetEvent('vehiclekeys:server:RemoveKey', function(id, netId) @@ -116,10 +68,11 @@ RegisterNetEvent('vehiclekeys:server:RemoveKey', function(id, netId) -- drop player end end) + exports('RemoveKey', RemoveKey) ---- Sets the door state to a desired value. ---- This event is expected to be called only by the server. +---Sets the door state to a desired value. +---This event is expected to be called only by the server. ---@param netId number The network ID of the entity. ---@param doorState number | nil Sets the door state if given RegisterNetEvent('vehiclekeys:server:SetDoorState', function(netId, doorState) @@ -129,9 +82,27 @@ RegisterNetEvent('vehiclekeys:server:SetDoorState', function(netId, doorState) -- drop player end end) + exports('SetDoorState', SetDoorState) ---- Gives a key to an entity based on the target player's CitizenID but only if the owner already has a key. +RegisterNetEvent('QBCore:Server:OnPlayerLoaded', function() + addPlayer(source --[[@as integer]]) +end) + +---@param src integer +RegisterNetEvent('QBCore:Server:OnPlayerUnload', function(src) + removePlayer(src) +end) + +AddEventHandler('playerDropped', function() + removePlayer(source --[[@as integer]]) +end) + +----------------------- +---- Callbacks ---- +----------------------- + +---Gives a key to an entity based on the target player's CitizenID but only if the owner already has a key. ---@param source number ID of the player ---@param netId number The network ID of the entity. ---@param targetPlayerId number ID of the target player who receives the key @@ -141,7 +112,7 @@ lib.callback.register('vehiclekeys:server:GiveKey', function(source, netId, targ -- This callback is not yet implemented end) ---- Removes a key from an entity based on the target player's CitizenID but only if the owner has a key. +---Removes a key from an entity based on the target player's CitizenID but only if the owner has a key. ---@param source number ID of the player ---@param netId number The network ID of the entity. ---@param targetPlayerId number ID of the target player who receives the key @@ -151,7 +122,7 @@ lib.callback.register('vehiclekeys:server:RemoveKey', function(source, netId, ta -- This callback is not yet implemented end) ---- Toggles the door state of the vehicle between open and closed. +---Toggles the door state of the vehicle between open and closed. ---@param source number ID of the player ---@param netId number The network ID of the entity ---@return number | nil -- Returns the current Door State @@ -159,8 +130,3 @@ lib.callback.register('vehiclekeys:server:ToggleDoorState', function(source, net if not source or not netId then return end -- This callback is not yet implemented end) - -AddEventHandler('QBCore:Server:PlayerLoaded', function(player) - local PlayerData = player.PlayerData - Player(PlayerData.source).state.keysList = keysList[PlayerData.citizenid] -end) diff --git a/shared/functions.lua b/shared/functions.lua index 66e31ab..798dde9 100644 --- a/shared/functions.lua +++ b/shared/functions.lua @@ -1,10 +1,10 @@ local public = {} --- Checks if the given two coordinates are close to each other based on distance. ---- @param coord1 vector3[] The first set of coordinates. ---- @param coord2 vector3[] The second set of coordinates. ---- @param distance number The maximum allowed distance for them to be considered close. ---- @return boolean true if the distance between two entities is less than the distance parameter. +---@param coord1 vector3[] The first set of coordinates. +---@param coord2 vector3[] The second set of coordinates. +---@param distance number The maximum allowed distance for them to be considered close. +---@return boolean true if the distance between two entities is less than the distance parameter. function public.isCloseToCoords(coord1, coord2, distance) return #(coord1 - coord2) < distance end