188 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
--[[
 | 
						|
    https://github.com/overextended/ox_lib
 | 
						|
 | 
						|
    This file is licensed under LGPL-3.0 or higher <https://www.gnu.org/licenses/lgpl-3.0.en.html>
 | 
						|
 | 
						|
    Copyright © 2025 Linden <https://github.com/thelindat>
 | 
						|
]]
 | 
						|
 | 
						|
---@class PointProperties
 | 
						|
---@field coords vector3
 | 
						|
---@field distance number
 | 
						|
---@field onEnter? fun(self: CPoint)
 | 
						|
---@field onExit? fun(self: CPoint)
 | 
						|
---@field nearby? fun(self: CPoint)
 | 
						|
---@field [string] any
 | 
						|
 | 
						|
---@class CPoint : PointProperties
 | 
						|
---@field id number
 | 
						|
---@field currentDistance number
 | 
						|
---@field isClosest? boolean
 | 
						|
---@field remove fun()
 | 
						|
 | 
						|
---@type table<number, CPoint>
 | 
						|
local points = {}
 | 
						|
---@type CPoint[]
 | 
						|
local nearbyPoints = {}
 | 
						|
local nearbyCount = 0
 | 
						|
---@type CPoint?
 | 
						|
local closestPoint
 | 
						|
local tick
 | 
						|
 | 
						|
local function removePoint(self)
 | 
						|
    if closestPoint?.id == self.id then
 | 
						|
        closestPoint = nil
 | 
						|
    end
 | 
						|
 | 
						|
    lib.grid.removeEntry(self)
 | 
						|
 | 
						|
    points[self.id] = nil
 | 
						|
end
 | 
						|
 | 
						|
CreateThread(function()
 | 
						|
    while true do
 | 
						|
        local coords = GetEntityCoords(cache.ped)
 | 
						|
        local newPoints = lib.grid.getNearbyEntries(coords, function(entry) return entry.remove == removePoint end) --[[@as CPoint[] ]]
 | 
						|
        local cellX, cellY = lib.grid.getCellPosition(coords)
 | 
						|
        cache.coords = coords
 | 
						|
        closestPoint = nil
 | 
						|
 | 
						|
        if cellX ~= cache.lastCellX or cellY ~= cache.lastCellY then
 | 
						|
            for i = 1, nearbyCount do
 | 
						|
                local point = nearbyPoints[i]
 | 
						|
 | 
						|
                if point.inside then
 | 
						|
                    local distance = #(coords - point.coords)
 | 
						|
 | 
						|
                    if distance > point.radius then
 | 
						|
                        if point.onExit then point:onExit() end
 | 
						|
 | 
						|
                        point.inside = nil
 | 
						|
                        point.currentDistance = nil
 | 
						|
                    end
 | 
						|
                end
 | 
						|
            end
 | 
						|
 | 
						|
            cache.lastCellX = cellX
 | 
						|
            cache.lastCellY = cellY
 | 
						|
        end
 | 
						|
 | 
						|
        if nearbyCount ~= 0 then
 | 
						|
            table.wipe(nearbyPoints)
 | 
						|
            nearbyCount = 0
 | 
						|
        end
 | 
						|
 | 
						|
        for i = 1, #newPoints do
 | 
						|
            local point = newPoints[i]
 | 
						|
            local distance = #(coords - point.coords)
 | 
						|
 | 
						|
            if distance <= point.radius then
 | 
						|
                point.currentDistance = distance
 | 
						|
 | 
						|
                if not closestPoint or distance < (closestPoint.currentDistance or point.radius) then
 | 
						|
                    if closestPoint then closestPoint.isClosest = nil end
 | 
						|
 | 
						|
                    point.isClosest = true
 | 
						|
                    closestPoint = point
 | 
						|
                end
 | 
						|
 | 
						|
                nearbyCount += 1
 | 
						|
                nearbyPoints[nearbyCount] = point
 | 
						|
 | 
						|
                if point.onEnter and not point.inside then
 | 
						|
                    point.inside = true
 | 
						|
                    point:onEnter()
 | 
						|
                end
 | 
						|
            elseif point.currentDistance then
 | 
						|
                if point.onExit then point:onExit() end
 | 
						|
 | 
						|
                point.inside = nil
 | 
						|
                point.currentDistance = nil
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        if not tick then
 | 
						|
            if nearbyCount ~= 0 then
 | 
						|
                tick = SetInterval(function()
 | 
						|
                    for i = nearbyCount, 1, -1 do
 | 
						|
                        local point = nearbyPoints[i]
 | 
						|
 | 
						|
                        if point and point.nearby then
 | 
						|
                            point:nearby()
 | 
						|
                        end
 | 
						|
                    end
 | 
						|
                end)
 | 
						|
            end
 | 
						|
        elseif nearbyCount == 0 then
 | 
						|
            tick = ClearInterval(tick)
 | 
						|
        end
 | 
						|
 | 
						|
        Wait(300)
 | 
						|
    end
 | 
						|
end)
 | 
						|
 | 
						|
local function toVector(coords)
 | 
						|
    local _type = type(coords)
 | 
						|
 | 
						|
    if _type ~= 'vector3' then
 | 
						|
        if _type == 'table' or _type == 'vector4' then
 | 
						|
            return vec3(coords[1] or coords.x, coords[2] or coords.y, coords[3] or coords.z)
 | 
						|
        end
 | 
						|
 | 
						|
        error(("expected type 'vector3' or 'table' (received %s)"):format(_type))
 | 
						|
    end
 | 
						|
 | 
						|
    return coords
 | 
						|
end
 | 
						|
 | 
						|
lib.points = {}
 | 
						|
 | 
						|
---@return CPoint
 | 
						|
---@overload fun(data: PointProperties): CPoint
 | 
						|
---@overload fun(coords: vector3, distance: number, data?: PointProperties): CPoint
 | 
						|
function lib.points.new(...)
 | 
						|
    local args = { ... }
 | 
						|
    local id = #points + 1
 | 
						|
    local self
 | 
						|
 | 
						|
    -- Support sending a single argument containing point data
 | 
						|
    if type(args[1]) == 'table' then
 | 
						|
        self = args[1]
 | 
						|
        self.id = id
 | 
						|
        self.remove = removePoint
 | 
						|
    else
 | 
						|
        -- Backwards compatibility for original implementation (args: coords, distance, data)
 | 
						|
        self = {
 | 
						|
            id = id,
 | 
						|
            coords = args[1],
 | 
						|
            remove = removePoint,
 | 
						|
        }
 | 
						|
    end
 | 
						|
 | 
						|
    self.coords = toVector(self.coords)
 | 
						|
    self.distance = self.distance or args[2]
 | 
						|
    self.radius = self.distance
 | 
						|
 | 
						|
    if args[3] then
 | 
						|
        for k, v in pairs(args[3]) do
 | 
						|
            self[k] = v
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    lib.grid.addEntry(self)
 | 
						|
    points[id] = self
 | 
						|
 | 
						|
    return self
 | 
						|
end
 | 
						|
 | 
						|
function lib.points.getAllPoints() return points end
 | 
						|
 | 
						|
function lib.points.getNearbyPoints() return nearbyPoints end
 | 
						|
 | 
						|
---@return CPoint?
 | 
						|
function lib.points.getClosestPoint() return closestPoint end
 | 
						|
 | 
						|
---@deprecated
 | 
						|
lib.points.closest = lib.points.getClosestPoint
 | 
						|
 | 
						|
return lib.points
 |