diff --git a/[core]/es_extended/client/compat.lua b/[core]/es_extended/client/compat.lua index 6981ff71..7fcc0e83 100644 --- a/[core]/es_extended/client/compat.lua +++ b/[core]/es_extended/client/compat.lua @@ -1,18 +1,62 @@ --All client-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: -ESX.Streaming.RequestModel = xLib.streaming.requestModel -ESX.Streaming.RequestStreamedTextureDict = xLib.streaming.requestStreamedTextureDict -ESX.Streaming.RequestNamedPtfxAsset = xLib.streaming.requestNamedPtfxAsset -ESX.Streaming.RequestAnimSet = xLib.streaming.requestAnimSet -ESX.Streaming.RequestAnimDict = xLib.streaming.requestAnimDict -ESX.Streaming.RequestWeaponAsset = xLib.streaming.requestWeaponAsset -ESX.Game.GetShapeTestResultSync = xLib.Raycast.GetShapeTestResult -ESX.Game.RaycastScreen = xLib.Raycast.FromScreen -ESX.Game.StartRaycasting = xLib.Raycast.Start ESX.Game.GetClosestEntity = xLib.entity.closest EnumerateEntitiesWithinDistance = xLib.entity.EnumerateWithinDistance ESX.Game.Teleport = xLib.entity.Teleport +ESX.Streaming = { + RequestModel = xLib.streaming.requestModel, + RequestStreamedTextureDict = xLib.streaming.requestStreamedTextureDict, + RequestNamedPtfxAsset = xLib.streaming.requestNamedPtfxAsset, + RequestAnimSet = xLib.streaming.requestAnimSet, + RequestAnimDict = xLib.streaming.requestAnimDict, + RequestWeaponAsset = xLib.streaming.requestWeaponAsset, +} + +ESX.Scaleform = { + ShowFreemodeMessage = xLib.scaleform.showFreemodeMessage, + ShowBreakingNews = xLib.scaleform.showBreakingNews, + ShowPopupWarning = xLib.scaleform.showPopupWarning, + ShowTrafficMovie = xLib.scaleform.showTrafficMovie, + Utils = { + RequestScaleformMovie = xLib.scaleform.utils.requestScaleformMovie, + RunMethod = xLib.scaleform.utils.runMethod, + }, +} + +ESX.CreatePointInternal = xLib.points.create +ESX.RemovePointInternal = xLib.points.remove +ESX.HidePointInternal = xLib.points.hide +StartPointsLoop = xLib.points.startLoop +ESX.Point = xLib.point + +ESX.RegisterInteraction = xLib.interactions.register +ESX.RemoveInteraction = xLib.interactions.remove +ESX.GetInteractKey = xLib.interactions.getInteractKey + +ESX.Game.GetPedMugshot = xLib.game.getPedMugshot +ESX.Game.SpawnObject = xLib.game.spawnObject +ESX.Game.SpawnLocalObject = xLib.game.spawnLocalObject +ESX.Game.DeleteVehicle = xLib.game.deleteVehicle +ESX.Game.DeleteObject = xLib.game.deleteObject +ESX.Game.SpawnVehicle = xLib.game.spawnVehicle +ESX.Game.SpawnLocalVehicle = xLib.game.spawnLocalVehicle +ESX.Game.IsVehicleEmpty = xLib.game.isVehicleEmpty +ESX.Game.GetObjects = xLib.game.getObjects +ESX.Game.GetPeds = xLib.game.getPeds +ESX.Game.GetVehicles = xLib.game.getVehicles +ESX.Game.GetPlayers = xLib.game.getPlayers +ESX.Game.GetClosestObject = xLib.game.getClosestObject +ESX.Game.GetClosestPed = xLib.game.getClosestPed +ESX.Game.GetClosestPlayer = xLib.game.getClosestPlayer +ESX.Game.GetClosestVehicle = xLib.game.getClosestVehicle +ESX.Game.GetPlayersInArea = xLib.game.getPlayersInArea +ESX.Game.GetVehiclesInArea = xLib.game.getVehiclesInArea +ESX.Game.IsSpawnPointClear = xLib.game.isSpawnPointClear +ESX.Game.GetVehicleInDirection = xLib.game.getVehicleInDirection +ESX.Game.GetVehicleProperties = xLib.game.getVehicleProperties +ESX.Game.SetVehicleProperties = xLib.game.setVehicleProperties + function ESX.RegisterInput(command_name, label, input_group, key, on_press, on_release) return xLib.addKeybind({ name = command_name, diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 745dda86..976b5fc3 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -443,676 +443,6 @@ function ESX.UI.ShowInventoryItemNotification(add, item, count) }) end ----@param ped integer The ped to get the mugshot of ----@param transparent? boolean Whether the mugshot should be transparent -function ESX.Game.GetPedMugshot(ped, transparent) - if not DoesEntityExist(ped) then - return - end - local mugshot = transparent and RegisterPedheadshotTransparent(ped) or RegisterPedheadshot(ped) - - while not IsPedheadshotReady(mugshot) do - Wait(0) - end - - return mugshot, GetPedheadshotTxdString(mugshot) -end - ----@param object integer | string The object to spawn ----@param coords table | vector3 The coords to spawn the object at ----@param cb? function The callback function ----@param networked? boolean Whether the object should be networked ----@return integer | nil -function ESX.Game.SpawnObject(object, coords, cb, networked) - local model = type(object) == "number" and object or joaat(object) - - ESX.Streaming.RequestModel(model) - - local obj = CreateObject(model, coords.x, coords.y, coords.z, networked == nil or networked, false, true) - return cb and cb(obj) or obj -end - ----@param object integer | string The object to spawn ----@param coords table | vector3 The coords to spawn the object at ----@param cb? function The callback function ----@return nil -function ESX.Game.SpawnLocalObject(object, coords, cb) - ESX.Game.SpawnObject(object, coords, cb, false) -end - ----@param vehicle integer The vehicle to delete ----@return nil -function ESX.Game.DeleteVehicle(vehicle) - SetEntityAsMissionEntity(vehicle, true, true) - DeleteVehicle(vehicle) -end - ----@param object integer The object to delete ----@return nil -function ESX.Game.DeleteObject(object) - SetEntityAsMissionEntity(object, false, true) - DeleteObject(object) -end - ----@param vehicleModel integer | string The vehicle to spawn ----@param coords table | vector3 The coords to spawn the vehicle at ----@param heading number The heading of the vehicle ----@param cb? fun(vehicle: number) The callback function ----@param networked? boolean Whether the vehicle should be networked ----@return number? vehicle -function ESX.Game.SpawnVehicle(vehicleModel, coords, heading, cb, networked) - if cb and not ESX.IsFunctionReference(cb) then - error("Invalid callback function") - end - - local model = type(vehicleModel) == "number" and vehicleModel or joaat(vehicleModel) - local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z) - local isNetworked = networked == nil or networked - - local playerCoords = GetEntityCoords(ESX.PlayerData.ped) - if not vector or not playerCoords then - return - end - - local dist = #(playerCoords - vector) - if dist > 424 then -- Onesync infinity Range (https://docs.fivem.net/docs/scripting-reference/onesync/) - local executingResource = GetInvokingResource() or "Unknown" - return error(("Resource ^5%s^1 Tried to spawn vehicle on the client but the position is too far away (Out of onesync range)."):format(executingResource)) - end - - local promise = not cb and promise.new() - CreateThread(function() - local modelHash = ESX.Streaming.RequestModel(model) - if not modelHash then - if promise then - promise:reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) - return - end - error(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) - end - - local vehicle = CreateVehicle(model, vector.x, vector.y, vector.z, heading, isNetworked, true) - - if networked then - local id = NetworkGetNetworkIdFromEntity(vehicle) - SetNetworkIdCanMigrate(id, true) - SetEntityAsMissionEntity(vehicle, true, true) - end - SetVehicleHasBeenOwnedByPlayer(vehicle, true) - SetVehicleNeedsToBeHotwired(vehicle, false) - SetModelAsNoLongerNeeded(model) - SetVehRadioStation(vehicle, "OFF") - - RequestCollisionAtCoord(vector.x, vector.y, vector.z) - while not HasCollisionLoadedAroundEntity(vehicle) do - Wait(0) - end - - if promise then - promise:resolve(vehicle) - elseif cb then - cb(vehicle) - end - end) - - if promise then - return Citizen.Await(promise) - end -end - ----@param vehicle integer The vehicle to spawn ----@param coords table | vector3 The coords to spawn the vehicle at ----@param heading number The heading of the vehicle ----@param cb? function The callback function ----@return nil -function ESX.Game.SpawnLocalVehicle(vehicle, coords, heading, cb) - ESX.Game.SpawnVehicle(vehicle, coords, heading, cb, false) -end - ----@param vehicle integer The vehicle to check ----@return boolean -function ESX.Game.IsVehicleEmpty(vehicle) - return GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1) -end - ----@return table -function ESX.Game.GetObjects() -- Leave the function for compatibility - return GetGamePool("CObject") -end - ----@param onlyOtherPeds? boolean Whether to exlude the player ped ----@return table -function ESX.Game.GetPeds(onlyOtherPeds) - local pool = GetGamePool("CPed") - - if onlyOtherPeds then - local myPed = ESX.PlayerData.ped - for i = 1, #pool do - if pool[i] == myPed then - table.remove(pool, i) - break - end - end - end - - return pool -end - ----@return table -function ESX.Game.GetVehicles() -- Leave the function for compatibility - return GetGamePool("CVehicle") -end - ----@param onlyOtherPlayers? boolean Whether to exclude the player ----@param returnKeyValue? boolean Whether to return the key value pair ----@param returnPeds? boolean Whether to return the peds ----@return table -function ESX.Game.GetPlayers(onlyOtherPlayers, returnKeyValue, returnPeds) - local players = {} - local active = GetActivePlayers() - - for i = 1, #active do - local currentPlayer = active[i] - local ped = GetPlayerPed(currentPlayer) - - if DoesEntityExist(ped) and ((onlyOtherPlayers and currentPlayer ~= ESX.playerId) or not onlyOtherPlayers) then - if returnKeyValue then - players[currentPlayer] = ped - else - players[#players + 1] = returnPeds and ped or currentPlayer - end - end - end - - return players -end - ----@param coords? table | vector3 The coords to get the closest object to ----@param modelFilter? table The model filter ----@return integer, integer -function ESX.Game.GetClosestObject(coords, modelFilter) - return ESX.Game.GetClosestEntity(ESX.Game.GetObjects(), false, coords, modelFilter) -end - ----@param coords? table | vector3 The coords to get the closest ped to ----@param modelFilter? table The model filter ----@return integer, integer -function ESX.Game.GetClosestPed(coords, modelFilter) - return ESX.Game.GetClosestEntity(ESX.Game.GetPeds(true), false, coords, modelFilter) -end - ----@param coords? table | vector3 The coords to get the closest player to ----@return integer, integer -function ESX.Game.GetClosestPlayer(coords) - return ESX.Game.GetClosestEntity(ESX.Game.GetPlayers(true, true), true, coords, nil) -end - ----@param coords? table | vector3 The coords to get the closest vehicle to ----@param modelFilter? table The model filter ----@return integer, integer -function ESX.Game.GetClosestVehicle(coords, modelFilter) - return ESX.Game.GetClosestEntity(ESX.Game.GetVehicles(), false, coords, modelFilter) -end - ----@param coords table | vector3 The coords to search from ----@param maxDistance number The max distance to search within ----@return table -function ESX.Game.GetPlayersInArea(coords, maxDistance) - return EnumerateEntitiesWithinDistance(ESX.Game.GetPlayers(true, true), true, coords, maxDistance) -end - ----@param coords table | vector3 The coords to search from ----@param maxDistance number The max distance to search within ----@return table -function ESX.Game.GetVehiclesInArea(coords, maxDistance) - return EnumerateEntitiesWithinDistance(ESX.Game.GetVehicles(), false, coords, maxDistance) -end - ----@param coords table | vector3 The coords to search from ----@param maxDistance number The max distance to search within ----@return boolean -function ESX.Game.IsSpawnPointClear(coords, maxDistance) - return #ESX.Game.GetVehiclesInArea(coords, maxDistance) == 0 -end - ----@return integer | nil, vector3 | nil -function ESX.Game.GetVehicleInDirection() - local _, hit, coords, _, _, entity = ESX.Game.RaycastScreen(5, 10, ESX.PlayerData.ped) - if hit and IsEntityAVehicle(entity) then - return entity, coords - end -end - ----@param vehicle integer The vehicle to get the properties of ----@return table | nil -function ESX.Game.GetVehicleProperties(vehicle) - if not DoesEntityExist(vehicle) then - return - end - - ---@type number | number[], number | number[] - local colorPrimary, colorSecondary = GetVehicleColours(vehicle) - local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) - local dashboardColor = GetVehicleDashboardColor(vehicle) - local interiorColor = GetVehicleInteriorColour(vehicle) - - if GetIsVehiclePrimaryColourCustom(vehicle) then - colorPrimary = { GetVehicleCustomPrimaryColour(vehicle) } - end - - if GetIsVehicleSecondaryColourCustom(vehicle) then - colorSecondary = { GetVehicleCustomSecondaryColour(vehicle) } - end - - local hasCustomXenonColor, customXenonColorR, customXenonColorG, customXenonColorB = GetVehicleXenonLightsCustomColor(vehicle) - local customXenonColor = nil - if hasCustomXenonColor then - customXenonColor = { customXenonColorR, customXenonColorG, customXenonColorB } - end - - local extras = {} - for extraId = 0, 20 do - if DoesExtraExist(vehicle, extraId) then - extras[tostring(extraId)] = IsVehicleExtraTurnedOn(vehicle, extraId) - end - end - - local doorsBroken, windowsBroken, tyreBurst = {}, {}, {} - local numWheels = tostring(GetVehicleNumberOfWheels(vehicle)) - - local TyresIndex = { -- Wheel index list according to the number of vehicle wheels. - ["2"] = { 0, 4 }, -- Bike and cycle. - ["3"] = { 0, 1, 4, 5 }, -- Vehicle with 3 wheels (get for wheels because some 3 wheels vehicles have 2 wheels on front and one rear or the reverse). - ["4"] = { 0, 1, 4, 5 }, -- Vehicle with 4 wheels. - ["6"] = { 0, 1, 2, 3, 4, 5 }, -- Vehicle with 6 wheels. - } - - if TyresIndex[numWheels] then - for _, idx in pairs(TyresIndex[numWheels]) do - tyreBurst[tostring(idx)] = IsVehicleTyreBurst(vehicle, idx, false) - end - end - - for windowId = 0, 7 do -- 13 - RollUpWindow(vehicle, windowId) --fix when you put the car away with the window down - windowsBroken[tostring(windowId)] = not IsVehicleWindowIntact(vehicle, windowId) - end - - local numDoors = GetNumberOfVehicleDoors(vehicle) - if numDoors and numDoors > 0 then - for doorsId = 0, numDoors do - doorsBroken[tostring(doorsId)] = IsVehicleDoorDamaged(vehicle, doorsId) - end - end - - return { - model = GetEntityModel(vehicle), - doorsBroken = doorsBroken, - windowsBroken = windowsBroken, - tyreBurst = tyreBurst, - tyresCanBurst = GetVehicleTyresCanBurst(vehicle), - plate = ESX.Math.Trim(GetVehicleNumberPlateText(vehicle)), - plateIndex = GetVehicleNumberPlateTextIndex(vehicle), - - bodyHealth = ESX.Math.Round(GetVehicleBodyHealth(vehicle), 1), - engineHealth = ESX.Math.Round(GetVehicleEngineHealth(vehicle), 1), - tankHealth = ESX.Math.Round(GetVehiclePetrolTankHealth(vehicle), 1), - - fuelLevel = ESX.Math.Round(GetVehicleFuelLevel(vehicle), 1), - dirtLevel = ESX.Math.Round(GetVehicleDirtLevel(vehicle), 1), - color1 = colorPrimary, - color2 = colorSecondary, - - pearlescentColor = pearlescentColor, - wheelColor = wheelColor, - - dashboardColor = dashboardColor, - interiorColor = interiorColor, - - wheels = GetVehicleWheelType(vehicle), - windowTint = GetVehicleWindowTint(vehicle), - xenonColor = GetVehicleXenonLightsColor(vehicle), - customXenonColor = customXenonColor, - - neonEnabled = { IsVehicleNeonLightEnabled(vehicle, 0), IsVehicleNeonLightEnabled(vehicle, 1), IsVehicleNeonLightEnabled(vehicle, 2), IsVehicleNeonLightEnabled(vehicle, 3) }, - - neonColor = table.pack(GetVehicleNeonLightsColour(vehicle)), - extras = extras, - tyreSmokeColor = table.pack(GetVehicleTyreSmokeColor(vehicle)), - - modSpoilers = GetVehicleMod(vehicle, 0), - modFrontBumper = GetVehicleMod(vehicle, 1), - modRearBumper = GetVehicleMod(vehicle, 2), - modSideSkirt = GetVehicleMod(vehicle, 3), - modExhaust = GetVehicleMod(vehicle, 4), - modFrame = GetVehicleMod(vehicle, 5), - modGrille = GetVehicleMod(vehicle, 6), - modHood = GetVehicleMod(vehicle, 7), - modFender = GetVehicleMod(vehicle, 8), - modRightFender = GetVehicleMod(vehicle, 9), - modRoof = GetVehicleMod(vehicle, 10), - modRoofLivery = GetVehicleRoofLivery(vehicle), - - modEngine = GetVehicleMod(vehicle, 11), - modBrakes = GetVehicleMod(vehicle, 12), - modTransmission = GetVehicleMod(vehicle, 13), - modHorns = GetVehicleMod(vehicle, 14), - modSuspension = GetVehicleMod(vehicle, 15), - modArmor = GetVehicleMod(vehicle, 16), - - modTurbo = IsToggleModOn(vehicle, 18), - modSmokeEnabled = IsToggleModOn(vehicle, 20), - modXenon = IsToggleModOn(vehicle, 22), - - modFrontWheels = GetVehicleMod(vehicle, 23), - modCustomFrontWheels = GetVehicleModVariation(vehicle, 23), - modBackWheels = GetVehicleMod(vehicle, 24), - modCustomBackWheels = GetVehicleModVariation(vehicle, 24), - - modPlateHolder = GetVehicleMod(vehicle, 25), - modVanityPlate = GetVehicleMod(vehicle, 26), - modTrimA = GetVehicleMod(vehicle, 27), - modOrnaments = GetVehicleMod(vehicle, 28), - modDashboard = GetVehicleMod(vehicle, 29), - modDial = GetVehicleMod(vehicle, 30), - modDoorSpeaker = GetVehicleMod(vehicle, 31), - modSeats = GetVehicleMod(vehicle, 32), - modSteeringWheel = GetVehicleMod(vehicle, 33), - modShifterLeavers = GetVehicleMod(vehicle, 34), - modAPlate = GetVehicleMod(vehicle, 35), - modSpeakers = GetVehicleMod(vehicle, 36), - modTrunk = GetVehicleMod(vehicle, 37), - modHydrolic = GetVehicleMod(vehicle, 38), - modEngineBlock = GetVehicleMod(vehicle, 39), - modAirFilter = GetVehicleMod(vehicle, 40), - modStruts = GetVehicleMod(vehicle, 41), - modArchCover = GetVehicleMod(vehicle, 42), - modAerials = GetVehicleMod(vehicle, 43), - modTrimB = GetVehicleMod(vehicle, 44), - modTank = GetVehicleMod(vehicle, 45), - modWindows = GetVehicleMod(vehicle, 46), - modLivery = GetVehicleMod(vehicle, 48) == -1 and GetVehicleLivery(vehicle) or GetVehicleMod(vehicle, 48), - modLightbar = GetVehicleMod(vehicle, 49), - } -end - ----@param vehicle integer The vehicle to set the properties of ----@param props table The properties to set ----@return nil -function ESX.Game.SetVehicleProperties(vehicle, props) - if not DoesEntityExist(vehicle) then - return - end - local colorPrimary, colorSecondary = GetVehicleColours(vehicle) - local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) - SetVehicleModKit(vehicle, 0) - - if props.tyresCanBurst ~= nil then - SetVehicleTyresCanBurst(vehicle, props.tyresCanBurst) - end - - if props.plate ~= nil then - SetVehicleNumberPlateText(vehicle, props.plate) - end - if props.plateIndex ~= nil then - SetVehicleNumberPlateTextIndex(vehicle, props.plateIndex) - end - if props.bodyHealth ~= nil then - SetVehicleBodyHealth(vehicle, props.bodyHealth + 0.0) - end - if props.engineHealth ~= nil then - SetVehicleEngineHealth(vehicle, props.engineHealth + 0.0) - end - if props.tankHealth ~= nil then - SetVehiclePetrolTankHealth(vehicle, props.tankHealth + 0.0) - end - if props.fuelLevel ~= nil then - SetVehicleFuelLevel(vehicle, props.fuelLevel + 0.0) - end - if props.dirtLevel ~= nil then - SetVehicleDirtLevel(vehicle, props.dirtLevel + 0.0) - end - if props.color1 ~= nil then - if type(props.color1) == "table" then - SetVehicleCustomPrimaryColour(vehicle, props.color1[1], props.color1[2], props.color1[3]) - else - SetVehicleColours(vehicle, props.color1, colorSecondary) - end - end - if props.color2 ~= nil then - if type(props.color2) == "table" then - SetVehicleCustomSecondaryColour(vehicle, props.color2[1], props.color2[2], props.color2[3]) - else - SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2) - end - end - if props.pearlescentColor ~= nil then - SetVehicleExtraColours(vehicle, props.pearlescentColor, wheelColor) - end - - if props.interiorColor ~= nil then - SetVehicleInteriorColor(vehicle, props.interiorColor) - end - - if props.dashboardColor ~= nil then - SetVehicleDashboardColor(vehicle, props.dashboardColor) - end - - if props.wheelColor ~= nil then - SetVehicleExtraColours(vehicle, props.pearlescentColor or pearlescentColor, props.wheelColor) - end - if props.wheels ~= nil then - SetVehicleWheelType(vehicle, props.wheels) - end - if props.windowTint ~= nil then - SetVehicleWindowTint(vehicle, props.windowTint) - end - - if props.neonEnabled ~= nil then - SetVehicleNeonLightEnabled(vehicle, 0, props.neonEnabled[1]) - SetVehicleNeonLightEnabled(vehicle, 1, props.neonEnabled[2]) - SetVehicleNeonLightEnabled(vehicle, 2, props.neonEnabled[3]) - SetVehicleNeonLightEnabled(vehicle, 3, props.neonEnabled[4]) - end - - if props.extras ~= nil then - for extraId, enabled in pairs(props.extras) do - extraId = tonumber(extraId) - if extraId then - SetVehicleExtra(vehicle, extraId, not enabled) - end - end - end - - if props.neonColor ~= nil then - SetVehicleNeonLightsColour(vehicle, props.neonColor[1], props.neonColor[2], props.neonColor[3]) - end - if props.xenonColor ~= nil then - SetVehicleXenonLightsColor(vehicle, props.xenonColor) - end - if props.customXenonColor ~= nil then - SetVehicleXenonLightsCustomColor(vehicle, props.customXenonColor[1], props.customXenonColor[2], props.customXenonColor[3]) - end - if props.modSmokeEnabled ~= nil then - ToggleVehicleMod(vehicle, 20, true) - end - if props.tyreSmokeColor ~= nil then - SetVehicleTyreSmokeColor(vehicle, props.tyreSmokeColor[1], props.tyreSmokeColor[2], props.tyreSmokeColor[3]) - end - if props.modSpoilers ~= nil then - SetVehicleMod(vehicle, 0, props.modSpoilers, false) - end - if props.modFrontBumper ~= nil then - SetVehicleMod(vehicle, 1, props.modFrontBumper, false) - end - if props.modRearBumper ~= nil then - SetVehicleMod(vehicle, 2, props.modRearBumper, false) - end - if props.modSideSkirt ~= nil then - SetVehicleMod(vehicle, 3, props.modSideSkirt, false) - end - if props.modExhaust ~= nil then - SetVehicleMod(vehicle, 4, props.modExhaust, false) - end - if props.modFrame ~= nil then - SetVehicleMod(vehicle, 5, props.modFrame, false) - end - if props.modGrille ~= nil then - SetVehicleMod(vehicle, 6, props.modGrille, false) - end - if props.modHood ~= nil then - SetVehicleMod(vehicle, 7, props.modHood, false) - end - if props.modFender ~= nil then - SetVehicleMod(vehicle, 8, props.modFender, false) - end - if props.modRightFender ~= nil then - SetVehicleMod(vehicle, 9, props.modRightFender, false) - end - if props.modRoof ~= nil then - SetVehicleMod(vehicle, 10, props.modRoof, false) - end - - if props.modRoofLivery ~= nil then - SetVehicleRoofLivery(vehicle, props.modRoofLivery) - end - - if props.modEngine ~= nil then - SetVehicleMod(vehicle, 11, props.modEngine, false) - end - if props.modBrakes ~= nil then - SetVehicleMod(vehicle, 12, props.modBrakes, false) - end - if props.modTransmission ~= nil then - SetVehicleMod(vehicle, 13, props.modTransmission, false) - end - if props.modHorns ~= nil then - SetVehicleMod(vehicle, 14, props.modHorns, false) - end - if props.modSuspension ~= nil then - SetVehicleMod(vehicle, 15, props.modSuspension, false) - end - if props.modArmor ~= nil then - SetVehicleMod(vehicle, 16, props.modArmor, false) - end - if props.modTurbo ~= nil then - ToggleVehicleMod(vehicle, 18, props.modTurbo) - end - if props.modXenon ~= nil then - ToggleVehicleMod(vehicle, 22, props.modXenon) - end - if props.modFrontWheels ~= nil then - SetVehicleMod(vehicle, 23, props.modFrontWheels, props.modCustomFrontWheels) - end - if props.modBackWheels ~= nil then - SetVehicleMod(vehicle, 24, props.modBackWheels, props.modCustomBackWheels) - end - if props.modPlateHolder ~= nil then - SetVehicleMod(vehicle, 25, props.modPlateHolder, false) - end - if props.modVanityPlate ~= nil then - SetVehicleMod(vehicle, 26, props.modVanityPlate, false) - end - if props.modTrimA ~= nil then - SetVehicleMod(vehicle, 27, props.modTrimA, false) - end - if props.modOrnaments ~= nil then - SetVehicleMod(vehicle, 28, props.modOrnaments, false) - end - if props.modDashboard ~= nil then - SetVehicleMod(vehicle, 29, props.modDashboard, false) - end - if props.modDial ~= nil then - SetVehicleMod(vehicle, 30, props.modDial, false) - end - if props.modDoorSpeaker ~= nil then - SetVehicleMod(vehicle, 31, props.modDoorSpeaker, false) - end - if props.modSeats ~= nil then - SetVehicleMod(vehicle, 32, props.modSeats, false) - end - if props.modSteeringWheel ~= nil then - SetVehicleMod(vehicle, 33, props.modSteeringWheel, false) - end - if props.modShifterLeavers ~= nil then - SetVehicleMod(vehicle, 34, props.modShifterLeavers, false) - end - if props.modAPlate ~= nil then - SetVehicleMod(vehicle, 35, props.modAPlate, false) - end - if props.modSpeakers ~= nil then - SetVehicleMod(vehicle, 36, props.modSpeakers, false) - end - if props.modTrunk ~= nil then - SetVehicleMod(vehicle, 37, props.modTrunk, false) - end - if props.modHydrolic ~= nil then - SetVehicleMod(vehicle, 38, props.modHydrolic, false) - end - if props.modEngineBlock ~= nil then - SetVehicleMod(vehicle, 39, props.modEngineBlock, false) - end - if props.modAirFilter ~= nil then - SetVehicleMod(vehicle, 40, props.modAirFilter, false) - end - if props.modStruts ~= nil then - SetVehicleMod(vehicle, 41, props.modStruts, false) - end - if props.modArchCover ~= nil then - SetVehicleMod(vehicle, 42, props.modArchCover, false) - end - if props.modAerials ~= nil then - SetVehicleMod(vehicle, 43, props.modAerials, false) - end - if props.modTrimB ~= nil then - SetVehicleMod(vehicle, 44, props.modTrimB, false) - end - if props.modTank ~= nil then - SetVehicleMod(vehicle, 45, props.modTank, false) - end - if props.modWindows ~= nil then - SetVehicleMod(vehicle, 46, props.modWindows, false) - end - - if props.modLivery ~= nil then - SetVehicleMod(vehicle, 48, props.modLivery, false) - SetVehicleLivery(vehicle, props.modLivery) - end - - if props.windowsBroken ~= nil then - for k, v in pairs(props.windowsBroken) do - if v then - k = tonumber(k) - if k then - RemoveVehicleWindow(vehicle, k) - end - end - end - end - - if props.doorsBroken ~= nil then - for k, v in pairs(props.doorsBroken) do - if v then - k = tonumber(k) - if k then - SetVehicleDoorBroken(vehicle, k, true) - end - end - end - end - - if props.tyreBurst ~= nil then - for k, v in pairs(props.tyreBurst) do - if v then - k = tonumber(k) - if k then - SetVehicleTyreBurst(vehicle, k, true, 1000.0) - end - end - end - end -end - ---@param coords vector3 | table coords to get the closest pickup to ---@param text string The text to display ---@param size? number The size of the text diff --git a/[core]/es_extended/client/imports/point.lua b/[core]/es_extended/client/imports/point.lua deleted file mode 100644 index afe42955..00000000 --- a/[core]/es_extended/client/imports/point.lua +++ /dev/null @@ -1,53 +0,0 @@ -local Point = ESX.Class() - -local nearby, loop = {}, nil - -function Point:constructor(properties) - self.coords = properties.coords - self.hidden = properties.hidden - self.enter = properties.enter - self.leave = properties.leave - self.inside = properties.inside - self.handle = ESX.CreatePointInternal(properties.coords, properties.distance, properties.hidden, function() - nearby[self.handle] = self - if self.enter then - self:enter() - end - if not loop then - loop = true - CreateThread(function() - while loop do - local coords = GetEntityCoords(ESX.PlayerData.ped) - for _, point in pairs(nearby) do - if point.inside then - point:inside(#(coords - point.coords)) - end - end - Wait(0) - end - end) - end - end, function() - nearby[self.handle] = nil - if self.leave then - self:leave() - end - if #nearby == 0 then - loop = false - end - end) -end - -function Point:delete() - ESX.RemovePointInternal(self.handle) -end - -function Point:toggle(hidden) - if hidden == nil then - hidden = not self.hidden - end - self.hidden = hidden - ESX.HidePointInternal(self.handle, hidden) -end - -return Point \ No newline at end of file diff --git a/[core]/es_extended/client/modules/actions.lua b/[core]/es_extended/client/modules/actions.lua index de174910..846e8307 100644 --- a/[core]/es_extended/client/modules/actions.lua +++ b/[core]/es_extended/client/modules/actions.lua @@ -1,10 +1,14 @@ +-- ESX glue over the generic xLib.cache. +-- ped and weapon are tracked by the lib cache; this module mirrors them into +-- ESX.PlayerData, re-emits the legacy esx: events resources depend on, and keeps +-- the vehicle enter/exit state machine that produces the richer vehicle events. + Actions = {} Actions._index = Actions Actions.inVehicle = false Actions.enteringVehicle = false Actions.inPauseMenu = false -Actions.currentWeapon = false function Actions:GetSeatPedIsIn() for i = -1, 16 do @@ -55,21 +59,6 @@ function Actions:TrackPedCoordsOnce() end) end -function Actions:TrackPed() - local playerPed = ESX.PlayerData.ped - local newPed = PlayerPedId() - - if playerPed ~= newPed then - ESX.SetPlayerData("ped", newPed) - - TriggerEvent("esx:playerPedChanged", newPed) - - if Config.EnableDebug then - print("[DEBUG] Player ped changed:", newPed) - end - end -end - function Actions:TrackPauseMenu() local isActive = IsPauseMenuActive() @@ -189,46 +178,43 @@ function Actions:TrackSeat() end end -function Actions:TrackWeapon() - ---@type number|false - local newWeapon = GetSelectedPedWeapon(ESX.PlayerData.ped) - newWeapon = newWeapon ~= `WEAPON_UNARMED` and newWeapon or false - - if newWeapon ~= self.currentWeapon then - self.currentWeapon = newWeapon - ESX.SetPlayerData("weapon", self.currentWeapon) - TriggerEvent("esx:weaponChanged", self.currentWeapon) - - if Config.EnableDebug then - print("[DEBUG] Weapon changed:", self.currentWeapon) - end - end -end - function Actions:SlowLoop() CreateThread(function() while ESX.PlayerLoaded do self:TrackPauseMenu() self:TrackVehicle() - self:TrackWeapon() Wait(500) end end) end -function Actions:PedLoop() - CreateThread(function() - while ESX.PlayerLoaded do - self:TrackPed() - Wait(0) - end - end) -end - function Actions:Init() + -- Re-seed the cached values on every (re)login. The change handlers below are + -- registered once at file load so a relogin never stacks duplicate handlers. + ESX.SetPlayerData("ped", xLib.cache.ped) + ESX.SetPlayerData("weapon", xLib.cache.weapon) + self:SlowLoop() - self:PedLoop() self:TrackPedCoordsOnce() end +-- Mirror the lib cache into ESX.PlayerData and re-emit the legacy esx: events. +AddEventHandler("xLib:cache:ped", function(ped) + ESX.SetPlayerData("ped", ped) + TriggerEvent("esx:playerPedChanged", ped) + + if Config.EnableDebug then + print("[DEBUG] Player ped changed:", ped) + end +end) + +AddEventHandler("xLib:cache:weapon", function(weapon) + ESX.SetPlayerData("weapon", weapon) + TriggerEvent("esx:weaponChanged", weapon) + + if Config.EnableDebug then + print("[DEBUG] Weapon changed:", weapon) + end +end) + Actions:Init() diff --git a/[core]/es_extended/client/modules/interactions.lua b/[core]/es_extended/client/modules/interactions.lua deleted file mode 100644 index 0a1a31fe..00000000 --- a/[core]/es_extended/client/modules/interactions.lua +++ /dev/null @@ -1,38 +0,0 @@ -local interactions = {} -local pressedInteractions = {} - -function ESX.RemoveInteraction(name) - if not interactions[name] then return end - interactions[name] = nil -end - -ESX.RegisterInteraction = function(name, onPress, condition) - interactions[name] = { - condition = condition or function() return true end, - onPress = onPress, - creator = GetInvokingResource() or "es_extended" - } -end - -function ESX.GetInteractKey() - local hash = joaat('esx_interact') | 0x80000000 - return GetControlInstructionalButton(0, hash, true):sub(3) -end - -ESX.RegisterInput("esx_interact", "Interact", "keyboard", "e", function() - for _, interaction in pairs(interactions) do - local success, result = pcall(interaction.condition) - if success and result then - pressedInteractions[#pressedInteractions+1] = interaction - interaction.onPress() - end - end -end) - -AddEventHandler("onResourceStop", function(resource) - for name, interaction in pairs(interactions) do - if interaction.creator == resource then - interactions[name] = nil - end - end -end) \ No newline at end of file diff --git a/[core]/es_extended/client/modules/points.lua b/[core]/es_extended/client/modules/points.lua deleted file mode 100644 index bbcaf8a8..00000000 --- a/[core]/es_extended/client/modules/points.lua +++ /dev/null @@ -1,54 +0,0 @@ -local points = {} - -function ESX.CreatePointInternal(coords, distance, hidden, enter, leave) - local point = { - coords = coords, - distance = distance, - hidden = hidden, - enter = enter, - leave = leave, - resource = GetInvokingResource() - } - local handle = ESX.Table.SizeOf(points) + 1 - points[handle] = point - return handle -end - -function ESX.RemovePointInternal(handle) - points[handle] = nil -end - -function ESX.HidePointInternal(handle, hidden) - if points[handle] then - points[handle].hidden = hidden - end -end - -function StartPointsLoop() - CreateThread(function() - while true do - local coords = GetEntityCoords(ESX.PlayerData.ped) - for handle, point in pairs(points) do - if not point.hidden and #(coords - point.coords) <= point.distance then - if not point.nearby then - points[handle].nearby = true - points[handle].enter() - end - elseif point.nearby then - points[handle].nearby = false - points[handle].leave() - end - end - Wait(500) - end - end) -end - - -AddEventHandler('onResourceStop', function(resource) - for handle, point in pairs(points) do - if point.resource == resource then - points[handle] = nil - end - end -end) \ No newline at end of file diff --git a/[core]/es_extended/fxmanifest.lua b/[core]/es_extended/fxmanifest.lua index b18cc12c..c68f54b4 100644 --- a/[core]/es_extended/fxmanifest.lua +++ b/[core]/es_extended/fxmanifest.lua @@ -52,16 +52,12 @@ client_scripts { 'client/modules/wrapper.lua', 'client/modules/callback.lua', 'client/modules/adjustments.lua', - 'client/modules/points.lua', 'client/modules/events.lua', 'client/modules/actions.lua', 'client/modules/death.lua', 'client/modules/npwd.lua', - 'client/modules/interactions.lua', - 'client/modules/scaleform.lua', - 'client/modules/streaming.lua', } ui_page { diff --git a/[core]/es_extended/imports.lua b/[core]/es_extended/imports.lua index 95797ba6..6ca908ee 100644 --- a/[core]/es_extended/imports.lua +++ b/[core]/es_extended/imports.lua @@ -87,7 +87,7 @@ if not IsDuplicityVersion() then -- Only register this event for the client end) end - local external = { { "Class", "class.lua" }, { "Point", "point.lua" } } + local external = { { "Class", "class.lua" } } for i = 1, #external do local module = external[i] local path = string.format("client/imports/%s", module[2]) diff --git a/[core]/es_extended/server/compat.lua b/[core]/es_extended/server/compat.lua index 8c638d6e..80fa0650 100644 --- a/[core]/es_extended/server/compat.lua +++ b/[core]/es_extended/server/compat.lua @@ -1,2 +1,10 @@ ---[[ All server-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: ---]] \ No newline at end of file +--All server-side functions outsourced from the Core to the lib will be stored here for compatability, e.g: + +ESX.OneSync.GetPlayersInArea = xLib.onesync.getPlayersInArea +ESX.OneSync.GetClosestPlayer = xLib.onesync.getClosestPlayer +ESX.OneSync.GetPedsInArea = xLib.onesync.getPedsInArea +ESX.OneSync.GetObjectsInArea = xLib.onesync.getObjectsInArea +ESX.OneSync.GetVehiclesInArea = xLib.onesync.getVehiclesInArea +ESX.OneSync.GetClosestPed = xLib.onesync.getClosestPed +ESX.OneSync.GetClosestObject = xLib.onesync.getClosestObject +ESX.OneSync.GetClosestVehicle = xLib.onesync.getClosestVehicle diff --git a/[core]/es_extended/server/modules/onesync.lua b/[core]/es_extended/server/modules/onesync.lua index 573dc888..18e179e5 100644 --- a/[core]/es_extended/server/modules/onesync.lua +++ b/[core]/es_extended/server/modules/onesync.lua @@ -1,84 +1,5 @@ ESX.OneSync = {} ----@param source number|vector3 ----@param closest boolean ----@param distance? number ----@param ignore? table ----@param routingBucket? number -local function getNearbyPlayers(source, closest, distance, ignore, routingBucket) - local result = {} - local count = 0 - local playerPed - local playerCoords - ignore = ignore or {} - - if not distance then - distance = 100 - end - - if type(source) == "number" then - playerPed = GetPlayerPed(source) - - if not source then - error("Received invalid first argument (source); should be playerId") - end - - playerCoords = GetEntityCoords(playerPed) - - if not playerCoords then - error("Received nil value (playerCoords); perhaps source is nil at first place?") - end - end - - if type(source) == "vector3" then - playerCoords = source - - if not playerCoords then - error("Received nil value (playerCoords); perhaps source is nil at first place?") - end - end - - for _, xPlayer in pairs(ESX.Players) do - if not ignore[xPlayer.source] and (not routingBucket or GetPlayerRoutingBucket(xPlayer.source) == routingBucket) then - local entity = GetPlayerPed(xPlayer.source) - local coords = GetEntityCoords(entity) - - if not closest then - local dist = #(playerCoords - coords) - if dist <= distance then - count = count + 1 - result[count] = { id = xPlayer.source, ped = NetworkGetNetworkIdFromEntity(entity), coords = coords, dist = dist } - end - else - if xPlayer.source ~= source then - local dist = #(playerCoords - coords) - if dist <= (result.dist or distance) then - result = { id = xPlayer.source, ped = NetworkGetNetworkIdFromEntity(entity), coords = coords, dist = dist } - end - end - end - end - end - - return result -end - ----@param source vector3|number playerId or vector3 coordinates ----@param maxDistance number ----@param ignore? table playerIds to ignore, where the key is playerId and value is true ----@param routingBucket? number -function ESX.OneSync.GetPlayersInArea(source, maxDistance, ignore, routingBucket) - return getNearbyPlayers(source, false, maxDistance, ignore, routingBucket) -end - ----@param source vector3|number playerId or vector3 coordinates ----@param maxDistance number ----@param ignore? table playerIds to ignore, where the key is playerId and value is true ----@param routingBucket? number -function ESX.OneSync.GetClosestPlayer(source, maxDistance, ignore, routingBucket) - return getNearbyPlayers(source, true, maxDistance, ignore, routingBucket) -end - ---@param vehicleModel number|string ---@param coords vector3|table ---@param heading number @@ -304,84 +225,3 @@ function ESX.OneSync.SpawnPedInVehicle(model, vehicle, seat, cb) return Citizen.Await(promise) end end - -local function getNearbyEntities(entities, coords, modelFilter, maxDistance, isPed) - local nearbyEntities = {} - coords = type(coords) == "number" and GetEntityCoords(GetPlayerPed(coords)) or vector3(coords.x, coords.y, coords.z) - for _, entity in pairs(entities) do - if not isPed or (isPed and not IsPedAPlayer(entity)) then - if not modelFilter or modelFilter[GetEntityModel(entity)] then - local entityCoords = GetEntityCoords(entity) - if not maxDistance or #(coords - entityCoords) <= maxDistance then - nearbyEntities[#nearbyEntities + 1] = NetworkGetNetworkIdFromEntity(entity) - end - end - end - end - - return nearbyEntities -end - ----@param coords vector3 ----@param maxDistance number ----@param modelFilter table models to ignore, where the key is the model hash and the value is true ----@return table -function ESX.OneSync.GetPedsInArea(coords, maxDistance, modelFilter) - return getNearbyEntities(GetAllPeds(), coords, modelFilter, maxDistance, true) -end - ----@param coords vector3 ----@param maxDistance number ----@param modelFilter table models to ignore, where the key is the model hash and the value is true ----@return table -function ESX.OneSync.GetObjectsInArea(coords, maxDistance, modelFilter) - return getNearbyEntities(GetAllObjects(), coords, modelFilter, maxDistance) -end - ----@param coords vector3 ----@param maxDistance number ----@param modelFilter table | nil models to ignore, where the key is the model hash and the value is true ----@return table -function ESX.OneSync.GetVehiclesInArea(coords, maxDistance, modelFilter) - return getNearbyEntities(GetAllVehicles(), coords, modelFilter, maxDistance) -end - -local function getClosestEntity(entities, coords, modelFilter, isPed) - local distance, closestEntity, closestCoords = 100, 0, vector3(0, 0, 0) - coords = type(coords) == "number" and GetEntityCoords(GetPlayerPed(coords)) or vector3(coords.x, coords.y, coords.z) - - for _, entity in pairs(entities) do - if not isPed or (isPed and not IsPedAPlayer(entity)) then - if not modelFilter or modelFilter[GetEntityModel(entity)] then - local entityCoords = GetEntityCoords(entity) - local dist = #(coords - entityCoords) - if dist < distance then - closestEntity, distance, closestCoords = entity, dist, entityCoords - end - end - end - end - - return NetworkGetNetworkIdFromEntity(closestEntity), distance, closestCoords -end - ----@param coords vector3 ----@param modelFilter table models to ignore, where the key is the model hash and the value is true ----@return number entityId, number distance, vector3 coords -function ESX.OneSync.GetClosestPed(coords, modelFilter) - return getClosestEntity(GetAllPeds(), coords, modelFilter, true) -end - ----@param coords vector3 ----@param modelFilter table models to ignore, where the key is the model hash and the value is true ----@return number entityId, number distance, vector3 coords -function ESX.OneSync.GetClosestObject(coords, modelFilter) - return getClosestEntity(GetAllObjects(), coords, modelFilter) -end - ----@param coords vector3 ----@param modelFilter table models to ignore, where the key is the model hash and the value is true ----@return number entityId, number distance, vector3 coords -function ESX.OneSync.GetClosestVehicle(coords, modelFilter) - return getClosestEntity(GetAllVehicles(), coords, modelFilter) -end diff --git a/[core]/esx_lib/imports/cache/client.lua b/[core]/esx_lib/imports/cache/client.lua new file mode 100644 index 00000000..5ab1c3d2 --- /dev/null +++ b/[core]/esx_lib/imports/cache/client.lua @@ -0,0 +1,89 @@ +--[[ + Generic client-side player state cache, modelled on ox_lib cache. + Framework agnostic: natives only, no ESX coupling. + + Loaded per-resource VM through the lazy loader. PlayerPedId() and friends + return the same value in every client VM, so the cached values are correct + in each resource that requires it. + + Exposes the live values as xLib.cache and emits `xLib:cache:` (value, previous) + whenever a tracked value changes. `coords` is read on demand from the live ped + and never stored. +]] + +local playerId = PlayerId() + +local cache = setmetatable({ + playerId = playerId, + ped = PlayerPedId(), + vehicle = false, + seat = false, + weapon = false, +}, { + __index = function(self, key) + if key == "coords" then + return GetEntityCoords(self.ped) + elseif key == "serverId" then + -- resolved lazily: GetPlayerServerId can return -1 before the network + -- session is ready, so only cache it once it is valid. + local id = GetPlayerServerId(playerId) + if id and id ~= -1 then + rawset(self, "serverId", id) + end + return id + end + end, +}) + +local function set(key, value) + if cache[key] == value then + return + end + + local previous = cache[key] + rawset(cache, key, value) + TriggerEvent(("xLib:cache:%s"):format(key), value, previous) +end + +local function getSeat(ped, vehicle) + for seat = -1, 16 do + if GetPedInVehicleSeat(vehicle, seat) == ped then + return seat + end + end + return false +end + +CreateThread(function() + while true do + local ped = PlayerPedId() + if ped ~= cache.ped then + set("ped", ped) + end + + ---@type integer|false + local vehicle = GetVehiclePedIsIn(ped, false) + vehicle = vehicle ~= 0 and vehicle or false + + if vehicle ~= cache.vehicle then + set("vehicle", vehicle) + set("seat", vehicle and getSeat(ped, vehicle) or false) + elseif vehicle then + local seat = getSeat(ped, vehicle) + if seat ~= cache.seat then + set("seat", seat) + end + end + + ---@type integer|false + local weapon = GetSelectedPedWeapon(ped) + weapon = weapon ~= `WEAPON_UNARMED` and weapon or false + if weapon ~= cache.weapon then + set("weapon", weapon) + end + + Wait(100) + end +end) + +return cache diff --git a/[core]/esx_lib/imports/game/client.lua b/[core]/esx_lib/imports/game/client.lua new file mode 100644 index 00000000..c7bdd1e8 --- /dev/null +++ b/[core]/esx_lib/imports/game/client.lua @@ -0,0 +1,695 @@ +---@class gamelib +xLib.game = {} + +local function isCallable(val) + local valType = type(val) + local mt = getmetatable(val) + return valType == "function" or (valType == "table" and mt ~= nil and type(mt.__call) == "function") +end + +---@param ped integer The ped to get the mugshot of +---@param transparent? boolean Whether the mugshot should be transparent +function xLib.game.getPedMugshot(ped, transparent) + if not DoesEntityExist(ped) then + return + end + + local mugshot = transparent and RegisterPedheadshotTransparent(ped) or RegisterPedheadshot(ped) + + while not IsPedheadshotReady(mugshot) do + Wait(0) + end + + return mugshot, GetPedheadshotTxdString(mugshot) +end + +---@param object integer | string The object to spawn +---@param coords table | vector3 The coords to spawn the object at +---@param cb? function The callback function +---@param networked? boolean Whether the object should be networked +---@return integer | nil +function xLib.game.spawnObject(object, coords, cb, networked) + local model = type(object) == "number" and object or joaat(object) + + xLib.streaming.requestModel(model) + + local obj = CreateObject(model, coords.x, coords.y, coords.z, networked == nil or networked, false, true) + return cb and cb(obj) or obj +end + +---@param object integer | string The object to spawn +---@param coords table | vector3 The coords to spawn the object at +---@param cb? function The callback function +---@return nil +function xLib.game.spawnLocalObject(object, coords, cb) + xLib.game.spawnObject(object, coords, cb, false) +end + +---@param vehicle integer The vehicle to delete +---@return nil +function xLib.game.deleteVehicle(vehicle) + SetEntityAsMissionEntity(vehicle, true, true) + DeleteVehicle(vehicle) +end + +---@param object integer The object to delete +---@return nil +function xLib.game.deleteObject(object) + SetEntityAsMissionEntity(object, false, true) + DeleteObject(object) +end + +---@param vehicleModel integer | string The vehicle to spawn +---@param coords table | vector3 The coords to spawn the vehicle at +---@param heading number The heading of the vehicle +---@param cb? fun(vehicle: number) The callback function +---@param networked? boolean Whether the vehicle should be networked +---@return number? vehicle +function xLib.game.spawnVehicle(vehicleModel, coords, heading, cb, networked) + if cb and not isCallable(cb) then + error("Invalid callback function") + end + + local model = type(vehicleModel) == "number" and vehicleModel or joaat(vehicleModel) + local vector = type(coords) == "vector3" and coords or vec(coords.x, coords.y, coords.z) + local isNetworked = networked == nil or networked + + local playerCoords = GetEntityCoords(PlayerPedId()) + if not vector or not playerCoords then + return + end + + local dist = #(playerCoords - vector) + if dist > 424 then -- Onesync infinity Range (https://docs.fivem.net/docs/scripting-reference/onesync/) + local executingResource = GetInvokingResource() or "Unknown" + return error(("Resource ^5%s^1 Tried to spawn vehicle on the client but the position is too far away (Out of onesync range)."):format(executingResource)) + end + + local promise = not cb and promise.new() + CreateThread(function() + local modelHash = xLib.streaming.requestModel(model) + if not modelHash then + if promise then + promise:reject(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) + return + end + error(("Tried to spawn invalid vehicle - ^5%s^7!"):format(model)) + end + + local vehicle = CreateVehicle(model, vector.x, vector.y, vector.z, heading, isNetworked, true) + + if networked then + local id = NetworkGetNetworkIdFromEntity(vehicle) + SetNetworkIdCanMigrate(id, true) + SetEntityAsMissionEntity(vehicle, true, true) + end + SetVehicleHasBeenOwnedByPlayer(vehicle, true) + SetVehicleNeedsToBeHotwired(vehicle, false) + SetModelAsNoLongerNeeded(model) + SetVehRadioStation(vehicle, "OFF") + + RequestCollisionAtCoord(vector.x, vector.y, vector.z) + while not HasCollisionLoadedAroundEntity(vehicle) do + Wait(0) + end + + if promise then + promise:resolve(vehicle) + elseif cb then + cb(vehicle) + end + end) + + if promise then + return Citizen.Await(promise) + end +end + +---@param vehicle integer The vehicle to spawn +---@param coords table | vector3 The coords to spawn the vehicle at +---@param heading number The heading of the vehicle +---@param cb? function The callback function +---@return nil +function xLib.game.spawnLocalVehicle(vehicle, coords, heading, cb) + xLib.game.spawnVehicle(vehicle, coords, heading, cb, false) +end + +---@param vehicle integer The vehicle to check +---@return boolean +function xLib.game.isVehicleEmpty(vehicle) + return GetVehicleNumberOfPassengers(vehicle) == 0 and IsVehicleSeatFree(vehicle, -1) +end + +---@return table +function xLib.game.getObjects() -- Leave the function for compatibility + return GetGamePool("CObject") +end + +---@param onlyOtherPeds? boolean Whether to exlude the player ped +---@return table +function xLib.game.getPeds(onlyOtherPeds) + local pool = GetGamePool("CPed") + + if onlyOtherPeds then + local myPed = PlayerPedId() + for i = 1, #pool do + if pool[i] == myPed then + table.remove(pool, i) + break + end + end + end + + return pool +end + +---@return table +function xLib.game.getVehicles() -- Leave the function for compatibility + return GetGamePool("CVehicle") +end + +---@param onlyOtherPlayers? boolean Whether to exclude the player +---@param returnKeyValue? boolean Whether to return the key value pair +---@param returnPeds? boolean Whether to return the peds +---@return table +function xLib.game.getPlayers(onlyOtherPlayers, returnKeyValue, returnPeds) + local players = {} + local active = GetActivePlayers() + + for i = 1, #active do + local currentPlayer = active[i] + local ped = GetPlayerPed(currentPlayer) + + if DoesEntityExist(ped) and ((onlyOtherPlayers and currentPlayer ~= PlayerId()) or not onlyOtherPlayers) then + if returnKeyValue then + players[currentPlayer] = ped + else + players[#players + 1] = returnPeds and ped or currentPlayer + end + end + end + + return players +end + +---@param coords? table | vector3 The coords to get the closest object to +---@param modelFilter? table The model filter +---@return integer, integer +function xLib.game.getClosestObject(coords, modelFilter) + return xLib.entity.closest(xLib.game.getObjects(), false, coords, modelFilter) +end + +---@param coords? table | vector3 The coords to get the closest ped to +---@param modelFilter? table The model filter +---@return integer, integer +function xLib.game.getClosestPed(coords, modelFilter) + return xLib.entity.closest(xLib.game.getPeds(true), false, coords, modelFilter) +end + +---@param coords? table | vector3 The coords to get the closest player to +---@return integer, integer +function xLib.game.getClosestPlayer(coords) + return xLib.entity.closest(xLib.game.getPlayers(true, true), true, coords, nil) +end + +---@param coords? table | vector3 The coords to get the closest vehicle to +---@param modelFilter? table The model filter +---@return integer, integer +function xLib.game.getClosestVehicle(coords, modelFilter) + return xLib.entity.closest(xLib.game.getVehicles(), false, coords, modelFilter) +end + +---@param coords table | vector3 The coords to search from +---@param maxDistance number The max distance to search within +---@return table +function xLib.game.getPlayersInArea(coords, maxDistance) + return xLib.entity.EnumerateWithinDistance(xLib.game.getPlayers(true, true), true, coords, maxDistance) +end + +---@param coords table | vector3 The coords to search from +---@param maxDistance number The max distance to search within +---@return table +function xLib.game.getVehiclesInArea(coords, maxDistance) + return xLib.entity.EnumerateWithinDistance(xLib.game.getVehicles(), false, coords, maxDistance) +end + +---@param coords table | vector3 The coords to search from +---@param maxDistance number The max distance to search within +---@return boolean +function xLib.game.isSpawnPointClear(coords, maxDistance) + return #xLib.game.getVehiclesInArea(coords, maxDistance) == 0 +end + +---@return integer | nil, vector3 | nil +function xLib.game.getVehicleInDirection() + local _, hit, coords, _, _, entity = xLib.Raycast.FromScreen(5, 10, PlayerPedId()) + if hit and IsEntityAVehicle(entity) then + return entity, coords + end +end + +local function round(value, numDecimalPlaces) + if numDecimalPlaces then + local power = 10 ^ numDecimalPlaces + return math.floor((value * power) + 0.5) / power + else + return math.floor(value + 0.5) + end +end + +local function trim(value) + value = tostring(value) + return (string.gsub(value, "^%s*(.-)%s*$", "%1")) +end + +---@param vehicle integer The vehicle to get the properties of +---@return table | nil +function xLib.game.getVehicleProperties(vehicle) + if not DoesEntityExist(vehicle) then + return + end + + ---@type number | number[], number | number[] + local colorPrimary, colorSecondary = GetVehicleColours(vehicle) + local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) + local dashboardColor = GetVehicleDashboardColor(vehicle) + local interiorColor = GetVehicleInteriorColour(vehicle) + + if GetIsVehiclePrimaryColourCustom(vehicle) then + colorPrimary = { GetVehicleCustomPrimaryColour(vehicle) } + end + + if GetIsVehicleSecondaryColourCustom(vehicle) then + colorSecondary = { GetVehicleCustomSecondaryColour(vehicle) } + end + + local hasCustomXenonColor, customXenonColorR, customXenonColorG, customXenonColorB = GetVehicleXenonLightsCustomColor(vehicle) + local customXenonColor = nil + if hasCustomXenonColor then + customXenonColor = { customXenonColorR, customXenonColorG, customXenonColorB } + end + + local extras = {} + for extraId = 0, 20 do + if DoesExtraExist(vehicle, extraId) then + extras[tostring(extraId)] = IsVehicleExtraTurnedOn(vehicle, extraId) + end + end + + local doorsBroken, windowsBroken, tyreBurst = {}, {}, {} + local numWheels = tostring(GetVehicleNumberOfWheels(vehicle)) + + local TyresIndex = { -- Wheel index list according to the number of vehicle wheels. + ["2"] = { 0, 4 }, -- Bike and cycle. + ["3"] = { 0, 1, 4, 5 }, -- Vehicle with 3 wheels (get for wheels because some 3 wheels vehicles have 2 wheels on front and one rear or the reverse). + ["4"] = { 0, 1, 4, 5 }, -- Vehicle with 4 wheels. + ["6"] = { 0, 1, 2, 3, 4, 5 }, -- Vehicle with 6 wheels. + } + + if TyresIndex[numWheels] then + for _, idx in pairs(TyresIndex[numWheels]) do + tyreBurst[tostring(idx)] = IsVehicleTyreBurst(vehicle, idx, false) + end + end + + for windowId = 0, 7 do -- 13 + RollUpWindow(vehicle, windowId) --fix when you put the car away with the window down + windowsBroken[tostring(windowId)] = not IsVehicleWindowIntact(vehicle, windowId) + end + + local numDoors = GetNumberOfVehicleDoors(vehicle) + if numDoors and numDoors > 0 then + for doorsId = 0, numDoors do + doorsBroken[tostring(doorsId)] = IsVehicleDoorDamaged(vehicle, doorsId) + end + end + + return { + model = GetEntityModel(vehicle), + doorsBroken = doorsBroken, + windowsBroken = windowsBroken, + tyreBurst = tyreBurst, + tyresCanBurst = GetVehicleTyresCanBurst(vehicle), + plate = trim(GetVehicleNumberPlateText(vehicle)), + plateIndex = GetVehicleNumberPlateTextIndex(vehicle), + + bodyHealth = round(GetVehicleBodyHealth(vehicle), 1), + engineHealth = round(GetVehicleEngineHealth(vehicle), 1), + tankHealth = round(GetVehiclePetrolTankHealth(vehicle), 1), + + fuelLevel = round(GetVehicleFuelLevel(vehicle), 1), + dirtLevel = round(GetVehicleDirtLevel(vehicle), 1), + color1 = colorPrimary, + color2 = colorSecondary, + + pearlescentColor = pearlescentColor, + wheelColor = wheelColor, + + dashboardColor = dashboardColor, + interiorColor = interiorColor, + + wheels = GetVehicleWheelType(vehicle), + windowTint = GetVehicleWindowTint(vehicle), + xenonColor = GetVehicleXenonLightsColor(vehicle), + customXenonColor = customXenonColor, + + neonEnabled = { IsVehicleNeonLightEnabled(vehicle, 0), IsVehicleNeonLightEnabled(vehicle, 1), IsVehicleNeonLightEnabled(vehicle, 2), IsVehicleNeonLightEnabled(vehicle, 3) }, + + neonColor = table.pack(GetVehicleNeonLightsColour(vehicle)), + extras = extras, + tyreSmokeColor = table.pack(GetVehicleTyreSmokeColor(vehicle)), + + modSpoilers = GetVehicleMod(vehicle, 0), + modFrontBumper = GetVehicleMod(vehicle, 1), + modRearBumper = GetVehicleMod(vehicle, 2), + modSideSkirt = GetVehicleMod(vehicle, 3), + modExhaust = GetVehicleMod(vehicle, 4), + modFrame = GetVehicleMod(vehicle, 5), + modGrille = GetVehicleMod(vehicle, 6), + modHood = GetVehicleMod(vehicle, 7), + modFender = GetVehicleMod(vehicle, 8), + modRightFender = GetVehicleMod(vehicle, 9), + modRoof = GetVehicleMod(vehicle, 10), + modRoofLivery = GetVehicleRoofLivery(vehicle), + + modEngine = GetVehicleMod(vehicle, 11), + modBrakes = GetVehicleMod(vehicle, 12), + modTransmission = GetVehicleMod(vehicle, 13), + modHorns = GetVehicleMod(vehicle, 14), + modSuspension = GetVehicleMod(vehicle, 15), + modArmor = GetVehicleMod(vehicle, 16), + + modTurbo = IsToggleModOn(vehicle, 18), + modSmokeEnabled = IsToggleModOn(vehicle, 20), + modXenon = IsToggleModOn(vehicle, 22), + + modFrontWheels = GetVehicleMod(vehicle, 23), + modCustomFrontWheels = GetVehicleModVariation(vehicle, 23), + modBackWheels = GetVehicleMod(vehicle, 24), + modCustomBackWheels = GetVehicleModVariation(vehicle, 24), + + modPlateHolder = GetVehicleMod(vehicle, 25), + modVanityPlate = GetVehicleMod(vehicle, 26), + modTrimA = GetVehicleMod(vehicle, 27), + modOrnaments = GetVehicleMod(vehicle, 28), + modDashboard = GetVehicleMod(vehicle, 29), + modDial = GetVehicleMod(vehicle, 30), + modDoorSpeaker = GetVehicleMod(vehicle, 31), + modSeats = GetVehicleMod(vehicle, 32), + modSteeringWheel = GetVehicleMod(vehicle, 33), + modShifterLeavers = GetVehicleMod(vehicle, 34), + modAPlate = GetVehicleMod(vehicle, 35), + modSpeakers = GetVehicleMod(vehicle, 36), + modTrunk = GetVehicleMod(vehicle, 37), + modHydrolic = GetVehicleMod(vehicle, 38), + modEngineBlock = GetVehicleMod(vehicle, 39), + modAirFilter = GetVehicleMod(vehicle, 40), + modStruts = GetVehicleMod(vehicle, 41), + modArchCover = GetVehicleMod(vehicle, 42), + modAerials = GetVehicleMod(vehicle, 43), + modTrimB = GetVehicleMod(vehicle, 44), + modTank = GetVehicleMod(vehicle, 45), + modWindows = GetVehicleMod(vehicle, 46), + modLivery = GetVehicleMod(vehicle, 48) == -1 and GetVehicleLivery(vehicle) or GetVehicleMod(vehicle, 48), + modLightbar = GetVehicleMod(vehicle, 49), + } +end + +---@param vehicle integer The vehicle to set the properties of +---@param props table The properties to set +---@return nil +function xLib.game.setVehicleProperties(vehicle, props) + if not DoesEntityExist(vehicle) then + return + end + local colorPrimary, colorSecondary = GetVehicleColours(vehicle) + local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle) + SetVehicleModKit(vehicle, 0) + + if props.tyresCanBurst ~= nil then + SetVehicleTyresCanBurst(vehicle, props.tyresCanBurst) + end + + if props.plate ~= nil then + SetVehicleNumberPlateText(vehicle, props.plate) + end + if props.plateIndex ~= nil then + SetVehicleNumberPlateTextIndex(vehicle, props.plateIndex) + end + if props.bodyHealth ~= nil then + SetVehicleBodyHealth(vehicle, props.bodyHealth + 0.0) + end + if props.engineHealth ~= nil then + SetVehicleEngineHealth(vehicle, props.engineHealth + 0.0) + end + if props.tankHealth ~= nil then + SetVehiclePetrolTankHealth(vehicle, props.tankHealth + 0.0) + end + if props.fuelLevel ~= nil then + SetVehicleFuelLevel(vehicle, props.fuelLevel + 0.0) + end + if props.dirtLevel ~= nil then + SetVehicleDirtLevel(vehicle, props.dirtLevel + 0.0) + end + if props.color1 ~= nil then + if type(props.color1) == "table" then + SetVehicleCustomPrimaryColour(vehicle, props.color1[1], props.color1[2], props.color1[3]) + else + SetVehicleColours(vehicle, props.color1, colorSecondary) + end + end + if props.color2 ~= nil then + if type(props.color2) == "table" then + SetVehicleCustomSecondaryColour(vehicle, props.color2[1], props.color2[2], props.color2[3]) + else + SetVehicleColours(vehicle, props.color1 or colorPrimary, props.color2) + end + end + if props.pearlescentColor ~= nil then + SetVehicleExtraColours(vehicle, props.pearlescentColor, wheelColor) + end + + if props.interiorColor ~= nil then + SetVehicleInteriorColor(vehicle, props.interiorColor) + end + + if props.dashboardColor ~= nil then + SetVehicleDashboardColor(vehicle, props.dashboardColor) + end + + if props.wheelColor ~= nil then + SetVehicleExtraColours(vehicle, props.pearlescentColor or pearlescentColor, props.wheelColor) + end + if props.wheels ~= nil then + SetVehicleWheelType(vehicle, props.wheels) + end + if props.windowTint ~= nil then + SetVehicleWindowTint(vehicle, props.windowTint) + end + + if props.neonEnabled ~= nil then + SetVehicleNeonLightEnabled(vehicle, 0, props.neonEnabled[1]) + SetVehicleNeonLightEnabled(vehicle, 1, props.neonEnabled[2]) + SetVehicleNeonLightEnabled(vehicle, 2, props.neonEnabled[3]) + SetVehicleNeonLightEnabled(vehicle, 3, props.neonEnabled[4]) + end + + if props.extras ~= nil then + for extraId, enabled in pairs(props.extras) do + extraId = tonumber(extraId) + if extraId then + SetVehicleExtra(vehicle, extraId, not enabled) + end + end + end + + if props.neonColor ~= nil then + SetVehicleNeonLightsColour(vehicle, props.neonColor[1], props.neonColor[2], props.neonColor[3]) + end + if props.xenonColor ~= nil then + SetVehicleXenonLightsColor(vehicle, props.xenonColor) + end + if props.customXenonColor ~= nil then + SetVehicleXenonLightsCustomColor(vehicle, props.customXenonColor[1], props.customXenonColor[2], props.customXenonColor[3]) + end + if props.modSmokeEnabled ~= nil then + ToggleVehicleMod(vehicle, 20, true) + end + if props.tyreSmokeColor ~= nil then + SetVehicleTyreSmokeColor(vehicle, props.tyreSmokeColor[1], props.tyreSmokeColor[2], props.tyreSmokeColor[3]) + end + if props.modSpoilers ~= nil then + SetVehicleMod(vehicle, 0, props.modSpoilers, false) + end + if props.modFrontBumper ~= nil then + SetVehicleMod(vehicle, 1, props.modFrontBumper, false) + end + if props.modRearBumper ~= nil then + SetVehicleMod(vehicle, 2, props.modRearBumper, false) + end + if props.modSideSkirt ~= nil then + SetVehicleMod(vehicle, 3, props.modSideSkirt, false) + end + if props.modExhaust ~= nil then + SetVehicleMod(vehicle, 4, props.modExhaust, false) + end + if props.modFrame ~= nil then + SetVehicleMod(vehicle, 5, props.modFrame, false) + end + if props.modGrille ~= nil then + SetVehicleMod(vehicle, 6, props.modGrille, false) + end + if props.modHood ~= nil then + SetVehicleMod(vehicle, 7, props.modHood, false) + end + if props.modFender ~= nil then + SetVehicleMod(vehicle, 8, props.modFender, false) + end + if props.modRightFender ~= nil then + SetVehicleMod(vehicle, 9, props.modRightFender, false) + end + if props.modRoof ~= nil then + SetVehicleMod(vehicle, 10, props.modRoof, false) + end + + if props.modRoofLivery ~= nil then + SetVehicleRoofLivery(vehicle, props.modRoofLivery) + end + + if props.modEngine ~= nil then + SetVehicleMod(vehicle, 11, props.modEngine, false) + end + if props.modBrakes ~= nil then + SetVehicleMod(vehicle, 12, props.modBrakes, false) + end + if props.modTransmission ~= nil then + SetVehicleMod(vehicle, 13, props.modTransmission, false) + end + if props.modHorns ~= nil then + SetVehicleMod(vehicle, 14, props.modHorns, false) + end + if props.modSuspension ~= nil then + SetVehicleMod(vehicle, 15, props.modSuspension, false) + end + if props.modArmor ~= nil then + SetVehicleMod(vehicle, 16, props.modArmor, false) + end + if props.modTurbo ~= nil then + ToggleVehicleMod(vehicle, 18, props.modTurbo) + end + if props.modXenon ~= nil then + ToggleVehicleMod(vehicle, 22, props.modXenon) + end + if props.modFrontWheels ~= nil then + SetVehicleMod(vehicle, 23, props.modFrontWheels, props.modCustomFrontWheels) + end + if props.modBackWheels ~= nil then + SetVehicleMod(vehicle, 24, props.modBackWheels, props.modCustomBackWheels) + end + if props.modPlateHolder ~= nil then + SetVehicleMod(vehicle, 25, props.modPlateHolder, false) + end + if props.modVanityPlate ~= nil then + SetVehicleMod(vehicle, 26, props.modVanityPlate, false) + end + if props.modTrimA ~= nil then + SetVehicleMod(vehicle, 27, props.modTrimA, false) + end + if props.modOrnaments ~= nil then + SetVehicleMod(vehicle, 28, props.modOrnaments, false) + end + if props.modDashboard ~= nil then + SetVehicleMod(vehicle, 29, props.modDashboard, false) + end + if props.modDial ~= nil then + SetVehicleMod(vehicle, 30, props.modDial, false) + end + if props.modDoorSpeaker ~= nil then + SetVehicleMod(vehicle, 31, props.modDoorSpeaker, false) + end + if props.modSeats ~= nil then + SetVehicleMod(vehicle, 32, props.modSeats, false) + end + if props.modSteeringWheel ~= nil then + SetVehicleMod(vehicle, 33, props.modSteeringWheel, false) + end + if props.modShifterLeavers ~= nil then + SetVehicleMod(vehicle, 34, props.modShifterLeavers, false) + end + if props.modAPlate ~= nil then + SetVehicleMod(vehicle, 35, props.modAPlate, false) + end + if props.modSpeakers ~= nil then + SetVehicleMod(vehicle, 36, props.modSpeakers, false) + end + if props.modTrunk ~= nil then + SetVehicleMod(vehicle, 37, props.modTrunk, false) + end + if props.modHydrolic ~= nil then + SetVehicleMod(vehicle, 38, props.modHydrolic, false) + end + if props.modEngineBlock ~= nil then + SetVehicleMod(vehicle, 39, props.modEngineBlock, false) + end + if props.modAirFilter ~= nil then + SetVehicleMod(vehicle, 40, props.modAirFilter, false) + end + if props.modStruts ~= nil then + SetVehicleMod(vehicle, 41, props.modStruts, false) + end + if props.modArchCover ~= nil then + SetVehicleMod(vehicle, 42, props.modArchCover, false) + end + if props.modAerials ~= nil then + SetVehicleMod(vehicle, 43, props.modAerials, false) + end + if props.modTrimB ~= nil then + SetVehicleMod(vehicle, 44, props.modTrimB, false) + end + if props.modTank ~= nil then + SetVehicleMod(vehicle, 45, props.modTank, false) + end + if props.modWindows ~= nil then + SetVehicleMod(vehicle, 46, props.modWindows, false) + end + + if props.modLivery ~= nil then + SetVehicleMod(vehicle, 48, props.modLivery, false) + SetVehicleLivery(vehicle, props.modLivery) + end + + if props.windowsBroken ~= nil then + for k, v in pairs(props.windowsBroken) do + if v then + k = tonumber(k) + if k then + RemoveVehicleWindow(vehicle, k) + end + end + end + end + + if props.doorsBroken ~= nil then + for k, v in pairs(props.doorsBroken) do + if v then + k = tonumber(k) + if k then + SetVehicleDoorBroken(vehicle, k, true) + end + end + end + end + + if props.tyreBurst ~= nil then + for k, v in pairs(props.tyreBurst) do + if v then + k = tonumber(k) + if k then + SetVehicleTyreBurst(vehicle, k, true, 1000.0) + end + end + end + end +end + +return xLib.game diff --git a/[core]/esx_lib/imports/interactions/client.lua b/[core]/esx_lib/imports/interactions/client.lua new file mode 100644 index 00000000..01b88c09 --- /dev/null +++ b/[core]/esx_lib/imports/interactions/client.lua @@ -0,0 +1,54 @@ +---@class interactionslib +xLib.interactions = {} + +local interactions = {} +local pressedInteractions = {} + +---@param name string +function xLib.interactions.remove(name) + if not interactions[name] then return end + interactions[name] = nil +end + +---@param name string +---@param onPress function +---@param condition? function +function xLib.interactions.register(name, onPress, condition) + interactions[name] = { + condition = condition or function() return true end, + onPress = onPress, + creator = GetInvokingResource() or "es_extended" + } +end + +---@return string +function xLib.interactions.getInteractKey() + local hash = joaat("esx_interact") | 0x80000000 + return GetControlInstructionalButton(0, hash, true):sub(3) +end + +xLib.addKeybind({ + name = "esx_interact", + description = "Interact", + defaultMapper = "keyboard", + defaultKey = "e", + onPressed = function() + for _, interaction in pairs(interactions) do + local success, result = pcall(interaction.condition) + if success and result then + pressedInteractions[#pressedInteractions + 1] = interaction + interaction.onPress() + end + end + end +}) + +AddEventHandler("onResourceStop", function(resource) + for name, interaction in pairs(interactions) do + if interaction.creator == resource then + interactions[name] = nil + end + end +end) + +return xLib.interactions diff --git a/[core]/esx_lib/imports/onesync/server.lua b/[core]/esx_lib/imports/onesync/server.lua new file mode 100644 index 00000000..c07be074 --- /dev/null +++ b/[core]/esx_lib/imports/onesync/server.lua @@ -0,0 +1,164 @@ +---@class onesynclib +xLib.onesync = {} + +---@param source number|vector3 +---@param closest boolean +---@param distance? number +---@param ignore? table +---@param routingBucket? number +local function getNearbyPlayers(source, closest, distance, ignore, routingBucket) + local result = {} + local count = 0 + local playerPed + local playerCoords + ignore = ignore or {} + + if not distance then + distance = 100 + end + + if type(source) == "number" then + playerPed = GetPlayerPed(source) + + if not source then + error("Received invalid first argument (source); should be playerId") + end + + playerCoords = GetEntityCoords(playerPed) + + if not playerCoords then + error("Received nil value (playerCoords); perhaps source is nil at first place?") + end + end + + if type(source) == "vector3" then + playerCoords = source + + if not playerCoords then + error("Received nil value (playerCoords); perhaps source is nil at first place?") + end + end + + for _, xPlayer in pairs(ESX.Players) do + if not ignore[xPlayer.source] and (not routingBucket or GetPlayerRoutingBucket(xPlayer.source) == routingBucket) then + local entity = GetPlayerPed(xPlayer.source) + local coords = GetEntityCoords(entity) + + if not closest then + local dist = #(playerCoords - coords) + if dist <= distance then + count = count + 1 + result[count] = { id = xPlayer.source, ped = NetworkGetNetworkIdFromEntity(entity), coords = coords, dist = dist } + end + else + if xPlayer.source ~= source then + local dist = #(playerCoords - coords) + if dist <= (result.dist or distance) then + result = { id = xPlayer.source, ped = NetworkGetNetworkIdFromEntity(entity), coords = coords, dist = dist } + end + end + end + end + end + + return result +end + +---@param source vector3|number playerId or vector3 coordinates +---@param maxDistance number +---@param ignore? table playerIds to ignore, where the key is playerId and value is true +---@param routingBucket? number +function xLib.onesync.getPlayersInArea(source, maxDistance, ignore, routingBucket) + return getNearbyPlayers(source, false, maxDistance, ignore, routingBucket) +end + +---@param source vector3|number playerId or vector3 coordinates +---@param maxDistance number +---@param ignore? table playerIds to ignore, where the key is playerId and value is true +---@param routingBucket? number +function xLib.onesync.getClosestPlayer(source, maxDistance, ignore, routingBucket) + return getNearbyPlayers(source, true, maxDistance, ignore, routingBucket) +end + +local function getNearbyEntities(entities, coords, modelFilter, maxDistance, isPed) + local nearbyEntities = {} + coords = type(coords) == "number" and GetEntityCoords(GetPlayerPed(coords)) or vector3(coords.x, coords.y, coords.z) + for _, entity in pairs(entities) do + if not isPed or (isPed and not IsPedAPlayer(entity)) then + if not modelFilter or modelFilter[GetEntityModel(entity)] then + local entityCoords = GetEntityCoords(entity) + if not maxDistance or #(coords - entityCoords) <= maxDistance then + nearbyEntities[#nearbyEntities + 1] = NetworkGetNetworkIdFromEntity(entity) + end + end + end + end + + return nearbyEntities +end + +---@param coords vector3 +---@param maxDistance number +---@param modelFilter table models to ignore, where the key is the model hash and the value is true +---@return table +function xLib.onesync.getPedsInArea(coords, maxDistance, modelFilter) + return getNearbyEntities(GetAllPeds(), coords, modelFilter, maxDistance, true) +end + +---@param coords vector3 +---@param maxDistance number +---@param modelFilter table models to ignore, where the key is the model hash and the value is true +---@return table +function xLib.onesync.getObjectsInArea(coords, maxDistance, modelFilter) + return getNearbyEntities(GetAllObjects(), coords, modelFilter, maxDistance) +end + +---@param coords vector3 +---@param maxDistance number +---@param modelFilter table | nil models to ignore, where the key is the model hash and the value is true +---@return table +function xLib.onesync.getVehiclesInArea(coords, maxDistance, modelFilter) + return getNearbyEntities(GetAllVehicles(), coords, modelFilter, maxDistance) +end + +local function getClosestEntity(entities, coords, modelFilter, isPed) + local distance, closestEntity, closestCoords = 100, 0, vector3(0, 0, 0) + coords = type(coords) == "number" and GetEntityCoords(GetPlayerPed(coords)) or vector3(coords.x, coords.y, coords.z) + + for _, entity in pairs(entities) do + if not isPed or (isPed and not IsPedAPlayer(entity)) then + if not modelFilter or modelFilter[GetEntityModel(entity)] then + local entityCoords = GetEntityCoords(entity) + local dist = #(coords - entityCoords) + if dist < distance then + closestEntity, distance, closestCoords = entity, dist, entityCoords + end + end + end + end + + return NetworkGetNetworkIdFromEntity(closestEntity), distance, closestCoords +end + +---@param coords vector3 +---@param modelFilter table models to ignore, where the key is the model hash and the value is true +---@return number entityId, number distance, vector3 coords +function xLib.onesync.getClosestPed(coords, modelFilter) + return getClosestEntity(GetAllPeds(), coords, modelFilter, true) +end + +---@param coords vector3 +---@param modelFilter table models to ignore, where the key is the model hash and the value is true +---@return number entityId, number distance, vector3 coords +function xLib.onesync.getClosestObject(coords, modelFilter) + return getClosestEntity(GetAllObjects(), coords, modelFilter) +end + +---@param coords vector3 +---@param modelFilter table models to ignore, where the key is the model hash and the value is true +---@return number entityId, number distance, vector3 coords +function xLib.onesync.getClosestVehicle(coords, modelFilter) + return getClosestEntity(GetAllVehicles(), coords, modelFilter) +end + +return xLib.onesync diff --git a/[core]/esx_lib/imports/point/client.lua b/[core]/esx_lib/imports/point/client.lua new file mode 100644 index 00000000..3ffb0063 --- /dev/null +++ b/[core]/esx_lib/imports/point/client.lua @@ -0,0 +1,52 @@ +---@class xPoint +---@field coords vector3 +---@field hidden? boolean +---@field enter? function +---@field leave? function +---@field inside? function +---@field handle integer +xLib.point = xLib.class() + +---@param properties table coords, distance, hidden, enter, leave, inside +function xLib.point:constructor(properties) + self.coords = properties.coords + self.hidden = properties.hidden + self.enter = properties.enter + self.leave = properties.leave + self.inside = properties.inside + + self.handle = xLib.points.create( + properties.coords, + properties.distance, + properties.hidden, + function() + if self.enter then + self:enter() + end + end, + function() + if self.leave then + self:leave() + end + end, + properties.inside and function(dist) + self:inside(dist) + end or nil + ) +end + +function xLib.point:delete() + xLib.points.remove(self.handle) +end + +---@param hidden? boolean +function xLib.point:toggle(hidden) + if hidden == nil then + hidden = not self.hidden + end + + self.hidden = hidden + xLib.points.hide(self.handle, hidden) +end + +return xLib.point diff --git a/[core]/esx_lib/imports/points/client.lua b/[core]/esx_lib/imports/points/client.lua new file mode 100644 index 00000000..7fd04eb1 --- /dev/null +++ b/[core]/esx_lib/imports/points/client.lua @@ -0,0 +1,100 @@ +---@class pointslib +xLib.points = {} + +local points = {} +local insidePoints = {} +local handleCount = 0 + +---@param coords vector3 +---@param distance number +---@param hidden? boolean +---@param enter function +---@param leave function +---@param inside? function +---@return integer handle +function xLib.points.create(coords, distance, hidden, enter, leave, inside) + handleCount = handleCount + 1 + local handle = handleCount + + points[handle] = { + coords = coords, + distance = distance, + hidden = hidden, + enter = enter, + leave = leave, + inside = inside, + resource = GetInvokingResource() + } + + return handle +end + +---@param handle integer +function xLib.points.remove(handle) + points[handle] = nil + insidePoints[handle] = nil +end + +---@param handle integer +---@param hidden boolean +function xLib.points.hide(handle, hidden) + if points[handle] then + points[handle].hidden = hidden + end +end + +function xLib.points.startLoop() + CreateThread(function() + local lastScan = 0 + + while true do + local coords = GetEntityCoords(PlayerPedId()) + + for _, point in pairs(insidePoints) do + point.inside(#(coords - point.coords)) + end + + local now = GetGameTimer() + if now - lastScan >= 500 then + lastScan = now + + for handle, point in pairs(points) do + if not point.hidden and #(coords - point.coords) <= point.distance then + if not point.nearby then + point.nearby = true + + if point.enter then + point.enter() + end + + if point.inside then + insidePoints[handle] = point + end + end + elseif point.nearby then + point.nearby = false + + if point.leave then + point.leave() + end + + insidePoints[handle] = nil + end + end + end + + Wait(next(insidePoints) and 0 or 500) + end + end) +end + +AddEventHandler("onResourceStop", function(resource) + for handle, point in pairs(points) do + if point.resource == resource then + points[handle] = nil + insidePoints[handle] = nil + end + end +end) + +return xLib.points diff --git a/[core]/es_extended/client/modules/scaleform.lua b/[core]/esx_lib/imports/scaleform/client.lua similarity index 61% rename from [core]/es_extended/client/modules/scaleform.lua rename to [core]/esx_lib/imports/scaleform/client.lua index 0662f9c3..e5729ced 100644 --- a/[core]/es_extended/client/modules/scaleform.lua +++ b/[core]/esx_lib/imports/scaleform/client.lua @@ -1,22 +1,12 @@ -ESX.Scaleform = {} -ESX.Scaleform.Utils = {} +---@class scaleformlib +xLib.scaleform = {} +xLib.scaleform.utils = {} -function ESX.Scaleform.ShowFreemodeMessage(title, msg, sec) - local scaleform = ESX.Scaleform.Utils.RunMethod("MP_BIG_MESSAGE_FREEMODE", "SHOW_SHARD_WASTED_MP_MESSAGE", false, title, msg) - - local endTime = GetGameTimer() + (sec * 1000) - while GetGameTimer() < endTime do - Wait(0) - DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) - end - - SetScaleformMovieAsNoLongerNeeded(scaleform) -end - -function ESX.Scaleform.ShowBreakingNews(title, msg, bottom, sec) - local scaleform = ESX.Scaleform.Utils.RunMethod("BREAKING_NEWS", "SET_TEXT", false, msg, bottom) - ESX.Scaleform.Utils.RunMethod(scaleform, "SET_SCROLL_TEXT", false, 0, 0, title) - ESX.Scaleform.Utils.RunMethod(scaleform, "DISPLAY_SCROLL_TEXT", false, 0, 0) +---@param title string +---@param msg string +---@param sec number +function xLib.scaleform.showFreemodeMessage(title, msg, sec) + local scaleform = xLib.scaleform.utils.runMethod("MP_BIG_MESSAGE_FREEMODE", "SHOW_SHARD_WASTED_MP_MESSAGE", false, title, msg) local endTime = GetGameTimer() + (sec * 1000) while GetGameTimer() < endTime do @@ -27,8 +17,14 @@ function ESX.Scaleform.ShowBreakingNews(title, msg, bottom, sec) SetScaleformMovieAsNoLongerNeeded(scaleform) end -function ESX.Scaleform.ShowPopupWarning(title, msg, bottom, sec) - local scaleform = ESX.Scaleform.Utils.RunMethod("POPUP_WARNING", "SHOW_POPUP_WARNING", false, 500.0, title, msg, bottom, true) +---@param title string +---@param msg string +---@param bottom string +---@param sec number +function xLib.scaleform.showBreakingNews(title, msg, bottom, sec) + local scaleform = xLib.scaleform.utils.runMethod("BREAKING_NEWS", "SET_TEXT", false, msg, bottom) + xLib.scaleform.utils.runMethod(scaleform, "SET_SCROLL_TEXT", false, 0, 0, title) + xLib.scaleform.utils.runMethod(scaleform, "DISPLAY_SCROLL_TEXT", false, 0, 0) local endTime = GetGameTimer() + (sec * 1000) while GetGameTimer() < endTime do @@ -39,8 +35,12 @@ function ESX.Scaleform.ShowPopupWarning(title, msg, bottom, sec) SetScaleformMovieAsNoLongerNeeded(scaleform) end -function ESX.Scaleform.ShowTrafficMovie(sec) - local scaleform = ESX.Scaleform.Utils.RunMethod("TRAFFIC_CAM", "PLAY_CAM_MOVIE", false) +---@param title string +---@param msg string +---@param bottom string +---@param sec number +function xLib.scaleform.showPopupWarning(title, msg, bottom, sec) + local scaleform = xLib.scaleform.utils.runMethod("POPUP_WARNING", "SHOW_POPUP_WARNING", false, 500.0, title, msg, bottom, true) local endTime = GetGameTimer() + (sec * 1000) while GetGameTimer() < endTime do @@ -51,7 +51,22 @@ function ESX.Scaleform.ShowTrafficMovie(sec) SetScaleformMovieAsNoLongerNeeded(scaleform) end -function ESX.Scaleform.Utils.RequestScaleformMovie(movie) +---@param sec number +function xLib.scaleform.showTrafficMovie(sec) + local scaleform = xLib.scaleform.utils.runMethod("TRAFFIC_CAM", "PLAY_CAM_MOVIE", false) + + local endTime = GetGameTimer() + (sec * 1000) + while GetGameTimer() < endTime do + Wait(0) + DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0) + end + + SetScaleformMovieAsNoLongerNeeded(scaleform) +end + +---@param movie string +---@return number +function xLib.scaleform.utils.requestScaleformMovie(movie) local scaleform = RequestScaleformMovie(movie) while not HasScaleformMovieLoaded(scaleform) do @@ -68,8 +83,11 @@ end ---@param returnValue? boolean # Whether to return the value from the method ---@param ... number|string|boolean # Arguments to pass to the method ---@return number, number? # The scaleform handle, and the return value if `returnValue` is true -function ESX.Scaleform.Utils.RunMethod(scaleform, methodName, returnValue, ...) - scaleform = type(scaleform) == "number" and scaleform or ESX.Scaleform.Utils.RequestScaleformMovie(scaleform) +function xLib.scaleform.utils.runMethod(scaleform, methodName, returnValue, ...) + if type(scaleform) ~= "number" then + scaleform = xLib.scaleform.utils.requestScaleformMovie(scaleform) + end + BeginScaleformMovieMethod(scaleform, methodName) local args = { ... } @@ -97,3 +115,5 @@ function ESX.Scaleform.Utils.RunMethod(scaleform, methodName, returnValue, ...) return scaleform end + +return xLib.scaleform diff --git a/[core]/esx_lib/imports/streaming/client.lua b/[core]/esx_lib/imports/streaming/client.lua index 38f6479e..535256f8 100644 --- a/[core]/esx_lib/imports/streaming/client.lua +++ b/[core]/esx_lib/imports/streaming/client.lua @@ -1,3 +1,4 @@ +---@class streaminglib xLib.streaming = {} ---@param modelHash number | string @@ -81,4 +82,4 @@ xLib.streaming.requestAudioBank = function(bankName, cb) end -return xLib.streaming \ No newline at end of file +return xLib.streaming diff --git a/[core]/esx_lib/resource/callback/shared.lua b/[core]/esx_lib/resource/callback/shared.lua index c8e0c7a2..aae0c0ca 100644 --- a/[core]/esx_lib/resource/callback/shared.lua +++ b/[core]/esx_lib/resource/callback/shared.lua @@ -8,6 +8,7 @@ local registeredCallbacks = {} local resource_name = GetCurrentResourceName() --TODO: Add cache +local IS_DEBUG = GetConvar("xLib:debug", "false") == "true" AddEventHandler('onResourceStop', function(resourceName) @@ -37,13 +38,19 @@ function xLib.setValidCallback(callbackName, isValid) if callbackResource == resourceName then return end - local errMessage = ("^1resource '%s' attempted to overwrite callback '%s' owned by resource '%s'^0"):format(resourceName, callbackName, callbackResource) + if IS_DEBUG then + local errMessage = ("^1resource '%s' attempted to overwrite callback '%s' owned by resource '%s'^0"):format(resourceName, callbackName, callbackResource) - return print(('^1SCRIPT ERROR: %s^0\n%s'):format(errMessage, - Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()) or '')) + print(('^1SCRIPT ERROR: %s^0\n%s'):format(errMessage, + Citizen.InvokeNative(`FORMAT_STACK_TRACE` & 0xFFFFFFFF, nil, 0, Citizen.ResultAsString()) or '')) + end + + return end - print(("set valid callback '%s' for resource '%s'"):format(callbackName, resourceName)) + if IS_DEBUG then + print(("set valid callback '%s' for resource '%s'"):format(callbackName, resourceName)) + end registeredCallbacks[callbackName] = resourceName end