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
 | 
