194 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			194 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
--[[
 | 
						|
    Based on PolyZone's grid system (https://github.com/mkafrin/PolyZone/blob/master/ComboZone.lua)
 | 
						|
 | 
						|
    MIT License
 | 
						|
 | 
						|
    Copyright © 2019-2021 Michael Afrin
 | 
						|
 | 
						|
    Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
						|
    of this software and associated documentation files (the "Software"), to deal
 | 
						|
    in the Software without restriction, including without limitation the rights
 | 
						|
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
						|
    copies of the Software, and to permit persons to whom the Software is
 | 
						|
    furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
    The above copyright notice and this permission notice shall be included in all
 | 
						|
    copies or substantial portions of the Software.
 | 
						|
 | 
						|
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
						|
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
						|
    SOFTWARE.
 | 
						|
]]
 | 
						|
 | 
						|
local mapMinX = -3700
 | 
						|
local mapMinY = -4400
 | 
						|
local mapMaxX = 4500
 | 
						|
local mapMaxY = 8000
 | 
						|
local xDelta = (mapMaxX - mapMinX) / 34
 | 
						|
local yDelta = (mapMaxY - mapMinY) / 50
 | 
						|
local grid = {}
 | 
						|
local lastCell = {}
 | 
						|
local gridCache = {}
 | 
						|
local entrySet = {}
 | 
						|
 | 
						|
lib.grid = {}
 | 
						|
 | 
						|
---@class GridEntry
 | 
						|
---@field coords vector
 | 
						|
---@field length? number
 | 
						|
---@field width? number
 | 
						|
---@field radius? number
 | 
						|
---@field [string] any
 | 
						|
 | 
						|
---@param point vector
 | 
						|
---@param length number
 | 
						|
---@param width number
 | 
						|
---@return number, number, number, number
 | 
						|
local function getGridDimensions(point, length, width)
 | 
						|
    local minX = (point.x - width - mapMinX) // xDelta
 | 
						|
    local maxX = (point.x + width - mapMinX) // xDelta
 | 
						|
    local minY = (point.y - length - mapMinY) // yDelta
 | 
						|
    local maxY = (point.y + length - mapMinY) // yDelta
 | 
						|
 | 
						|
    return minX, maxX, minY, maxY
 | 
						|
end
 | 
						|
 | 
						|
---@param point vector
 | 
						|
---@return number, number
 | 
						|
function lib.grid.getCellPosition(point)
 | 
						|
    local x = (point.x - mapMinX) // xDelta
 | 
						|
    local y = (point.y - mapMinY) // yDelta
 | 
						|
 | 
						|
    return x, y
 | 
						|
end
 | 
						|
 | 
						|
---@param point vector
 | 
						|
---@return GridEntry[]
 | 
						|
function lib.grid.getCell(point)
 | 
						|
    local x, y = lib.grid.getCellPosition(point)
 | 
						|
 | 
						|
    if lastCell.x ~= x or lastCell.y ~= y then
 | 
						|
        lastCell.x = x
 | 
						|
        lastCell.y = y
 | 
						|
        lastCell.cell = grid[y] and grid[y][x] or {}
 | 
						|
    end
 | 
						|
 | 
						|
    return lastCell.cell
 | 
						|
end
 | 
						|
 | 
						|
---@param point vector
 | 
						|
---@param filter? fun(entry: GridEntry): boolean
 | 
						|
---@return Array<GridEntry>
 | 
						|
function lib.grid.getNearbyEntries(point, filter)
 | 
						|
    local minX, maxX, minY, maxY = getGridDimensions(point, xDelta, yDelta)
 | 
						|
 | 
						|
    if gridCache.filter == filter and
 | 
						|
        gridCache.minX == minX and
 | 
						|
        gridCache.maxX == maxX and
 | 
						|
        gridCache.minY == minY and
 | 
						|
        gridCache.maxY == maxY then
 | 
						|
        return gridCache.entries
 | 
						|
    end
 | 
						|
 | 
						|
    local entries = lib.array:new()
 | 
						|
    local n = 0
 | 
						|
 | 
						|
    table.wipe(entrySet)
 | 
						|
 | 
						|
    for y = minY, maxY do
 | 
						|
        local row = grid[y]
 | 
						|
 | 
						|
        for x = minX, maxX do
 | 
						|
            local cell = row and row[x]
 | 
						|
 | 
						|
            if cell then
 | 
						|
                for j = 1, #cell do
 | 
						|
                    local entry = cell[j]
 | 
						|
 | 
						|
                    if not entrySet[entry] and (not filter or filter(entry)) then
 | 
						|
                        n = n + 1
 | 
						|
                        entrySet[entry] = true
 | 
						|
                        entries[n] = entry
 | 
						|
                    end
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
    end
 | 
						|
 | 
						|
    gridCache.minX = minX
 | 
						|
    gridCache.maxX = maxX
 | 
						|
    gridCache.minY = minY
 | 
						|
    gridCache.maxY = maxY
 | 
						|
    gridCache.entries = entries
 | 
						|
    gridCache.filter = filter
 | 
						|
 | 
						|
    return entries
 | 
						|
end
 | 
						|
 | 
						|
---@param entry { coords: vector, length?: number, width?: number, radius?: number, [string]: any }
 | 
						|
function lib.grid.addEntry(entry)
 | 
						|
    entry.length = entry.length or entry.radius * 2
 | 
						|
    entry.width = entry.width or entry.radius * 2
 | 
						|
    local minX, maxX, minY, maxY = getGridDimensions(entry.coords, entry.length, entry.width)
 | 
						|
 | 
						|
    for y = minY, maxY do
 | 
						|
        local row = grid[y] or {}
 | 
						|
 | 
						|
        for x = minX, maxX do
 | 
						|
            local cell = row[x] or {}
 | 
						|
 | 
						|
            cell[#cell + 1] = entry
 | 
						|
            row[x] = cell
 | 
						|
        end
 | 
						|
 | 
						|
        grid[y] = row
 | 
						|
 | 
						|
        table.wipe(gridCache)
 | 
						|
    end
 | 
						|
end
 | 
						|
 | 
						|
---@param entry table A table that was added to the grid previously.
 | 
						|
function lib.grid.removeEntry(entry)
 | 
						|
    local minX, maxX, minY, maxY = getGridDimensions(entry.coords, entry.length, entry.width)
 | 
						|
    local success = false
 | 
						|
 | 
						|
    for y = minY, maxY do
 | 
						|
        local row = grid[y]
 | 
						|
 | 
						|
        if not row then goto continue end
 | 
						|
 | 
						|
        for x = minX, maxX do
 | 
						|
            local cell = row[x]
 | 
						|
 | 
						|
            if cell then
 | 
						|
                for i = 1, #cell do
 | 
						|
                    if cell[i] == entry then
 | 
						|
                        table.remove(cell, i)
 | 
						|
                        success = true
 | 
						|
                        break
 | 
						|
                    end
 | 
						|
                end
 | 
						|
 | 
						|
                if #cell == 0 then
 | 
						|
                    row[x] = nil
 | 
						|
                end
 | 
						|
            end
 | 
						|
        end
 | 
						|
 | 
						|
        if not next(row) then
 | 
						|
            grid[y] = nil
 | 
						|
        end
 | 
						|
 | 
						|
        ::continue::
 | 
						|
    end
 | 
						|
 | 
						|
    table.wipe(gridCache)
 | 
						|
 | 
						|
    return success
 | 
						|
end
 | 
						|
 | 
						|
return lib.grid
 |