diff --git a/lib/init.lua b/lib/init.lua index e22e837..23f448f 100644 --- a/lib/init.lua +++ b/lib/init.lua @@ -47,6 +47,7 @@ cLib = { Math = Math or Require("lib/utility/shared/math.lua"), LA = LA or Require("lib/utility/shared/la.lua"), Perlin = Perlin or Require("lib/utility/shared/perlin.lua"), + -- Zones = Poly or Require("lib/zones/shared/zones.lua"), -- Action = Action or Require("lib/entities/shared/actions.lua"), } @@ -71,7 +72,7 @@ cLib.Placeable = Placeable or Require("lib/placers/client/object_placer.lua") cLib.Utility = Utility or Require("lib/utility/client/utility.lua") cLib.PlaceableObject = ObjectPlacer or Require("lib/placers/client/placeable_object.lua") cLib.Raycast = Raycast or Require("lib/raycast/client/raycast.lua") -cLib.Point = Point or Require("lib/points/client/points.lua") +cLib.Point = Point or Require("lib/points/client/pointsv2.lua") cLib.Particle = Particle or Require("lib/particles/client/particles.lua") cLib.Cache = Cache or Require("lib/cache/client/cache.lua") cLib.ClientEntity = ClientEntity or Require("lib/entities/client/client_entity.lua") diff --git a/lib/points/client/points.lua b/lib/points/client/points.lua index 79d2d2f..569d29c 100644 --- a/lib/points/client/points.lua +++ b/lib/points/client/points.lua @@ -1,255 +1,255 @@ --- Grid-based point system -Point = {} -local ActivePoints = {} -local GridCells = {} -local LoopStarted = false -local insidePoints = {} -local data = {} -- 👈 Move this outside the function +-- -- Grid-based point system +-- Point = {} +-- local ActivePoints = {} +-- local GridCells = {} +-- local LoopStarted = false +-- local insidePoints = {} +-- local data = {} -- 👈 Move this outside the function --- Grid configuration -local GRID_SIZE = 500.0 -- Size of each grid cell -local CELL_BUFFER = 1 -- Number of adjacent cells to check +-- -- Grid configuration +-- local GRID_SIZE = 500.0 -- Size of each grid cell +-- local CELL_BUFFER = 1 -- Number of adjacent cells to check --- Consider adding these optimizations -local ADAPTIVE_WAIT = false -- Adjust wait time based on player speed +-- -- Consider adding these optimizations +-- local ADAPTIVE_WAIT = false -- Adjust wait time based on player speed ----This is an internal function, do not call this externally -function Point.GetCellKey(coords) - local cellX = math.floor(coords.x / GRID_SIZE) - local cellY = math.floor(coords.y / GRID_SIZE) - return cellX .. ":" .. cellY -end +-- ---This is an internal function, do not call this externally +-- function Point.GetCellKey(coords) +-- local cellX = math.floor(coords.x / GRID_SIZE) +-- local cellY = math.floor(coords.y / GRID_SIZE) +-- return cellX .. ":" .. cellY +-- end ----This is an internal function, do not call this externally -function Point.RegisterInGrid(point) - local cellKey = Point.GetCellKey(point.coords) +-- ---This is an internal function, do not call this externally +-- function Point.RegisterInGrid(point) +-- local cellKey = Point.GetCellKey(point.coords) - -- Initialize cell if it doesn't exist - GridCells[cellKey] = GridCells[cellKey] or { - points = {}, - count = 0 - } +-- -- Initialize cell if it doesn't exist +-- GridCells[cellKey] = GridCells[cellKey] or { +-- points = {}, +-- count = 0 +-- } - -- Add point to cell - GridCells[cellKey].points[point.id] = point - GridCells[cellKey].count = GridCells[cellKey].count + 1 +-- -- Add point to cell +-- GridCells[cellKey].points[point.id] = point +-- GridCells[cellKey].count = GridCells[cellKey].count + 1 - -- Store cell reference in point - point.cellKey = cellKey -end +-- -- Store cell reference in point +-- point.cellKey = cellKey +-- end ----This is an internal function, do not call this externally -function Point.UpdateInGrid(point, oldCellKey) - -- Remove from old cell if cell key changed - if oldCellKey and oldCellKey ~= point.cellKey then - if GridCells[oldCellKey] and GridCells[oldCellKey].points[point.id] then - GridCells[oldCellKey].points[point.id] = nil - GridCells[oldCellKey].count = GridCells[oldCellKey].count - 1 +-- ---This is an internal function, do not call this externally +-- function Point.UpdateInGrid(point, oldCellKey) +-- -- Remove from old cell if cell key changed +-- if oldCellKey and oldCellKey ~= point.cellKey then +-- if GridCells[oldCellKey] and GridCells[oldCellKey].points[point.id] then +-- GridCells[oldCellKey].points[point.id] = nil +-- GridCells[oldCellKey].count = GridCells[oldCellKey].count - 1 - -- Clean up empty cells - if GridCells[oldCellKey].count <= 0 then - GridCells[oldCellKey] = nil - end - end +-- -- Clean up empty cells +-- if GridCells[oldCellKey].count <= 0 then +-- GridCells[oldCellKey] = nil +-- end +-- end - -- Add to new cell - Point.RegisterInGrid(point) - end -end +-- -- Add to new cell +-- Point.RegisterInGrid(point) +-- end +-- end ----Gets nearby cells based on the coords ----@param coords table ----@return table -function Point.GetNearbyCells(coords) - local cellX = math.floor(coords.x / GRID_SIZE) - local cellY = math.floor(coords.y / GRID_SIZE) - local nearbyCells = {} +-- ---Gets nearby cells based on the coords +-- ---@param coords table +-- ---@return table +-- function Point.GetNearbyCells(coords) +-- local cellX = math.floor(coords.x / GRID_SIZE) +-- local cellY = math.floor(coords.y / GRID_SIZE) +-- local nearbyCells = {} - -- Get current and adjacent cells - for x = cellX - CELL_BUFFER, cellX + CELL_BUFFER do - for y = cellY - CELL_BUFFER, cellY + CELL_BUFFER do - local key = x .. ":" .. y - if GridCells[key] then - table.insert(nearbyCells, key) - end - end - end +-- -- Get current and adjacent cells +-- for x = cellX - CELL_BUFFER, cellX + CELL_BUFFER do +-- for y = cellY - CELL_BUFFER, cellY + CELL_BUFFER do +-- local key = x .. ":" .. y +-- if GridCells[key] then +-- table.insert(nearbyCells, key) +-- end +-- end +-- end - return nearbyCells -end +-- return nearbyCells +-- end ----Returns all points in the same cell as the given point ----This will require you to pass the point object ----@param point table ----@return table -function Point.CheckPointsInSameCell(point) - local cellKey = point.cellKey - if not GridCells[cellKey] then return {} end +-- ---Returns all points in the same cell as the given point +-- ---This will require you to pass the point object +-- ---@param point table +-- ---@return table +-- function Point.CheckPointsInSameCell(point) +-- local cellKey = point.cellKey +-- if not GridCells[cellKey] then return {} end - local nearbyPoints = {} - for id, otherPoint in pairs(GridCells[cellKey].points) do - if id ~= point.id then - local distance = #(point.coords - otherPoint.coords) - if distance < (point.distance + otherPoint.distance) then - nearbyPoints[id] = otherPoint - end - end - end +-- local nearbyPoints = {} +-- for id, otherPoint in pairs(GridCells[cellKey].points) do +-- if id ~= point.id then +-- local distance = #(point.coords - otherPoint.coords) +-- if distance < (point.distance + otherPoint.distance) then +-- nearbyPoints[id] = otherPoint +-- end +-- end +-- end - return nearbyPoints -end +-- return nearbyPoints +-- end ----Internal function that starts the loop. Do not call this function directly. -function Point.StartLoop() - if LoopStarted then return false end - LoopStarted = true - -- Remove the "local data = {}" line from here - CreateThread(function() - while LoopStarted do - local playerPed = PlayerPedId() - while playerPed == -1 do - Wait(100) - playerPed = PlayerPedId() - end - local playerCoords = GetEntityCoords(playerPed) - local targetsExist = false - local playerCellKey = Point.GetCellKey(playerCoords) - local nearbyCells = Point.GetNearbyCells(playerCoords) +-- ---Internal function that starts the loop. Do not call this function directly. +-- function Point.StartLoop() +-- if LoopStarted then return false end +-- LoopStarted = true +-- -- Remove the "local data = {}" line from here +-- CreateThread(function() +-- while LoopStarted do +-- local playerPed = PlayerPedId() +-- while playerPed == -1 do +-- Wait(100) +-- playerPed = PlayerPedId() +-- end +-- local playerCoords = GetEntityCoords(playerPed) +-- local targetsExist = false +-- local playerCellKey = Point.GetCellKey(playerCoords) +-- local nearbyCells = Point.GetNearbyCells(playerCoords) - local playerSpeed = GetEntitySpeed(playerPed) - local maxWeight = 1000 - local waitTime = ADAPTIVE_WAIT and math.max(maxWeight/10, maxWeight - playerSpeed * maxWeight/10) or maxWeight - -- Process only points in nearby cells - for _, cellKey in ipairs(nearbyCells) do - if GridCells[cellKey] then - for id, point in pairs(GridCells[cellKey].points) do - targetsExist = true +-- local playerSpeed = GetEntitySpeed(playerPed) +-- local maxWeight = 1000 +-- local waitTime = ADAPTIVE_WAIT and math.max(maxWeight/10, maxWeight - playerSpeed * maxWeight/10) or maxWeight +-- -- Process only points in nearby cells +-- for _, cellKey in ipairs(nearbyCells) do +-- if GridCells[cellKey] then +-- for id, point in pairs(GridCells[cellKey].points) do +-- targetsExist = true - -- Update entity position if needed - local oldCellKey = point.cellKey - if point.isEntity then - point.coords = GetEntityCoords(point.target) - point.cellKey = Point.GetCellKey(point.coords) - Point.UpdateInGrid(point, oldCellKey) - end - local coords = point.coords and vector3(point.coords.x, point.coords.y, point.coords.z) or vector3(0, 0, 0) - local distance = #(playerCoords - coords) - --local distance = #(playerCoords - point.coords) +-- -- Update entity position if needed +-- local oldCellKey = point.cellKey +-- if point.isEntity then +-- point.coords = GetEntityCoords(point.target) +-- point.cellKey = Point.GetCellKey(point.coords) +-- Point.UpdateInGrid(point, oldCellKey) +-- end +-- local coords = point.coords and vector3(point.coords.x, point.coords.y, point.coords.z) or vector3(0, 0, 0) +-- local distance = #(playerCoords - coords) +-- --local distance = #(playerCoords - point.coords) - -- Check if player entered/exited the point - if distance < point.distance then - if not point.inside then - point.inside = true - data[point.id] = data[point.id] or point.args or {} - data[point.id] = point.onEnter(point, data[point.id]) - insidePoints[point.id] = point - end - -- Modified main loop exit handler - elseif point.inside then - point.inside = false - data[point.id] = data[point.id] or point.args or {} - local result = point.onExit(point, data[point.id]) - data[point.id] = result ~= nil and result or data[point.id] -- ← Use consistent fallback - point.args = data[point.id] -- ← Update point.args to match data[point.id] - insidePoints[point.id] = nil - end +-- -- Check if player entered/exited the point +-- if distance < point.distance then +-- if not point.inside then +-- point.inside = true +-- data[point.id] = data[point.id] or point.args or {} +-- data[point.id] = point.onEnter(point, data[point.id]) +-- insidePoints[point.id] = point +-- end +-- -- Modified main loop exit handler +-- elseif point.inside then +-- point.inside = false +-- data[point.id] = data[point.id] or point.args or {} +-- local result = point.onExit(point, data[point.id]) +-- data[point.id] = result ~= nil and result or data[point.id] -- ← Use consistent fallback +-- point.args = data[point.id] -- ← Update point.args to match data[point.id] +-- insidePoints[point.id] = nil +-- end - -- if point.onNearby then - -- point.onNearby(GridCells[cellKey]?.points, waitTime) - -- end - end - end - end +-- -- if point.onNearby then +-- -- point.onNearby(GridCells[cellKey]?.points, waitTime) +-- -- end +-- end +-- end +-- end - for id, insidepoint in pairs(insidePoints) do - local pos = insidepoint.coords and vector3(insidepoint.coords.x, insidepoint.coords.y, insidepoint.coords.z) or vector3(0, 0, 0) - local dist = #(playerCoords - pos) - if dist > insidepoint.distance then - insidepoint.inside = false - data[insidepoint.id] = data[insidepoint.id] or insidepoint.args or {} - local result = insidepoint.onExit(insidepoint, data[insidepoint.id]) - data[insidepoint.id] = result ~= nil and result or data[insidepoint.id] - insidepoint.args = data[insidepoint.id] -- ← Keep data in sync - insidePoints[insidepoint.id] = nil - end - end - Wait(waitTime) -- Faster updates when moving quickly - end - end) - return true -end +-- for id, insidepoint in pairs(insidePoints) do +-- local pos = insidepoint.coords and vector3(insidepoint.coords.x, insidepoint.coords.y, insidepoint.coords.z) or vector3(0, 0, 0) +-- local dist = #(playerCoords - pos) +-- if dist > insidepoint.distance then +-- insidepoint.inside = false +-- data[insidepoint.id] = data[insidepoint.id] or insidepoint.args or {} +-- local result = insidepoint.onExit(insidepoint, data[insidepoint.id]) +-- data[insidepoint.id] = result ~= nil and result or data[insidepoint.id] +-- insidepoint.args = data[insidepoint.id] -- ← Keep data in sync +-- insidePoints[insidepoint.id] = nil +-- end +-- end +-- Wait(waitTime) -- Faster updates when moving quickly +-- end +-- end) +-- return true +-- end ----Create a point based on a vector or entityID ----@param id string ----@param target number || vector3 ----@param distance number ----@param _onEnter function ----@param _onExit function ----@param _onNearby function ----@param data self ----@return table -function Point.Register(id, target, distance, args, _onEnter, _onExit, _onNearby) - local isEntity = type(target) == "number" - local coords = isEntity and GetEntityCoords(target) or target +-- ---Create a point based on a vector or entityID +-- ---@param id string +-- ---@param target number || vector3 +-- ---@param distance number +-- ---@param _onEnter function +-- ---@param _onExit function +-- ---@param _onNearby function +-- ---@param data self +-- ---@return table +-- function Point.Register(id, target, distance, args, _onEnter, _onExit, _onNearby) +-- local isEntity = type(target) == "number" +-- local coords = isEntity and GetEntityCoords(target) or target - local self = {} - self.id = id - self.target = target -- Store entity ID or Vector3 - self.isEntity = isEntity - self.coords = coords - self.distance = distance - self.onEnter = _onEnter or function() end - self.onExit = _onExit or function() end - self.onNearby = _onNearby or function() end - self.inside = false -- Track if player is inside - self.args = args or {} +-- local self = {} +-- self.id = id +-- self.target = target -- Store entity ID or Vector3 +-- self.isEntity = isEntity +-- self.coords = coords +-- self.distance = distance +-- self.onEnter = _onEnter or function() end +-- self.onExit = _onExit or function() end +-- self.onNearby = _onNearby or function() end +-- self.inside = false -- Track if player is inside +-- self.args = args or {} - ActivePoints[id] = self - Point.RegisterInGrid(self) - Point.StartLoop() - return self -end +-- ActivePoints[id] = self +-- Point.RegisterInGrid(self) +-- Point.StartLoop() +-- return self +-- end ----Remove an exsisting point by its id ----@param id string -function Point.Remove(id) - local point = ActivePoints[id] - if point then - local cellKey = point.cellKey - if GridCells[cellKey] and GridCells[cellKey].points[id] then - GridCells[cellKey].points[id] = nil - GridCells[cellKey].count = GridCells[cellKey].count - 1 +-- ---Remove an exsisting point by its id +-- ---@param id string +-- function Point.Remove(id) +-- local point = ActivePoints[id] +-- if point then +-- local cellKey = point.cellKey +-- if GridCells[cellKey] and GridCells[cellKey].points[id] then +-- GridCells[cellKey].points[id] = nil +-- GridCells[cellKey].count = GridCells[cellKey].count - 1 - if GridCells[cellKey].count <= 0 then - GridCells[cellKey] = nil - end - end +-- if GridCells[cellKey].count <= 0 then +-- GridCells[cellKey] = nil +-- end +-- end - ActivePoints[id] = nil - end -end +-- ActivePoints[id] = nil +-- end +-- end ----Returnes a point by its id ----@param id string ----@return table -function Point.Get(id) - return ActivePoints[id] -end +-- ---Returnes a point by its id +-- ---@param id string +-- ---@return table +-- function Point.Get(id) +-- return ActivePoints[id] +-- end -function Point.UpdateCoords(id, coords) - local point = ActivePoints[id] - if point then - point.coords = coords - local oldCellKey = point.cellKey - point.cellKey = Point.GetCellKey(coords) - Point.UpdateInGrid(point, oldCellKey) - end -end +-- function Point.UpdateCoords(id, coords) +-- local point = ActivePoints[id] +-- if point then +-- point.coords = coords +-- local oldCellKey = point.cellKey +-- point.cellKey = Point.GetCellKey(coords) +-- Point.UpdateInGrid(point, oldCellKey) +-- end +-- end ----Returns all points ----@return table -function Point.GetAll() - return ActivePoints -end +-- ---Returns all points +-- ---@return table +-- function Point.GetAll() +-- return ActivePoints +-- end -return Point \ No newline at end of file +-- return Point \ No newline at end of file diff --git a/lib/points/client/pointsv2.lua b/lib/points/client/pointsv2.lua new file mode 100644 index 0000000..1af43fd --- /dev/null +++ b/lib/points/client/pointsv2.lua @@ -0,0 +1,152 @@ + +local GRIDE_SIZE_X = 8968.83 / 2 +local GRIDE_SIZE_Y = 126200.40 / 2 +local CELL_SIZE = 500 + +local Grid = {} +Grid.Generated = false +local Grids = {} +Point = {} +Point.All = {} + +function Grid.Generate() + if Grid.Generated then return end + Grid.Generated = true + local numOfCellsX = math.ceil(GRIDE_SIZE_X / CELL_SIZE) + local numOfCellsY = math.ceil(GRIDE_SIZE_Y / CELL_SIZE) + + for x = -numOfCellsX, numOfCellsX do + Grids[x] = {} + for y = -numOfCellsY, numOfCellsY do + Grids[x][y] = {} + end + end +end + +function Grid.GetCellId(coords) + local cellX = math.floor(coords.x / CELL_SIZE) + 1 + local cellY = math.floor(coords.y / CELL_SIZE) + 1 + return string.format("%d_%d", cellX, cellY) +end + +function Grid.GetCellByCoords(coords) + local cellX = math.floor(coords.x / CELL_SIZE) + 1 + local cellY = math.floor(coords.y / CELL_SIZE) + 1 + + if Grids[cellX] and Grids[cellX][cellY] then + return Grids[cellX][cellY] + end + + return nil +end + +local Points = {} +Points.Started = false +function Point.StartLoop() + if Points.Started then return end + Points.Started = true + + CreateThread(function() + while Points.Started do + local playerPed = PlayerPedId() + if playerPed ~= -1 then + local coords = GetEntityCoords(playerPed) + local cell = Grid.GetCellByCoords(coords) + for _, point in pairs(cell or {}) do + if point.isEntity then + local entity = point.target + if DoesEntityExist(entity) then + local entityCoords = GetEntityCoords(entity) + local distance = #(coords - entityCoords) + if distance < point.distance then + if not point.inside then + point.inside = true + point.args = point?.onEnter(point, point.args) or point.args + end + elseif point.inside then + point.inside = false + point.args = point?.onExit(point, point.args) or point.args + end + end + else + local distance = #(coords.xyz - point.coords.xyz) + if distance < point.distance then + if not point.inside then + point.inside = true + point.args = point?.onEnter(point, point.args) or point.args + end + elseif point.inside then + point.inside = false + point.args = point?.onExit(point, point.args) or point.args + end + end + end + end + Wait(1000) + end + end) +end + +function Point.Register(id, target, distance, args, _onEnter, _onExit) + local isEntity = type(target) == "number" + local coords = isEntity and GetEntityCoords(target) or target + print(target) + Grid.Generate() + local self = {} + self.id = id + self.target = target -- Store entity ID or Vector3 + self.isEntity = isEntity + self.coords = coords + self.distance = distance + self.onEnter = _onEnter or function() end + self.onExit = _onExit or function() end + self.inside = false -- Track if player is inside + self.args = args or {} + local grid = Grid.GetCellByCoords(coords) + self.cellKey = Grid.GetCellId(coords) + grid[self.cellKey] = self + Point.All[id] = self + Point.StartLoop() + return self +end + +function Point.Remove(id) + local point = Point.All[id] + if not point then return false end + + -- Remove from grid + local grid = Grid.GetCellByCoords(point.coords) + if grid then + grid[point.cellKey] = nil + end + + Point.All[id] = nil + return true +end + +function Point.Get(id) + return Point.All[id] +end + +function Point.UpdateCoords(id, coords) + local point = Point.All[id] + if not point then return end + + local newGrid = Grid.GetCellByCoords(coords) + if not newGrid or newGrid[point.cellKey] then return end + local oldGrid = Grid.GetCellByCoords(point.coords) + if oldGrid then + oldGrid[point.cellKey] = nil + end + -- Add to new grid + point.coords = coords + point.cellKey = string.format("%d_%d", newGrid.x, newGrid.y) + newGrid[point.cellKey] = point + return true +end + +function Point.GetAll() + return Point.All +end + +return Point \ No newline at end of file