From 534b1ab435bbfaa35a31c4c4e6200c10a3830659 Mon Sep 17 00:00:00 2001 From: Linden <65407488+thelindat@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:29:14 +1100 Subject: [PATCH] feat(grid): add a grid system Based on mkafrin's grid used by PolyZone's ComboZones. --- imports/grid/shared.lua | 187 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 imports/grid/shared.lua diff --git a/imports/grid/shared.lua b/imports/grid/shared.lua new file mode 100644 index 0000000..9355f59 --- /dev/null +++ b/imports/grid/shared.lua @@ -0,0 +1,187 @@ +--[[ + Based on PolyZone's grid system (https://github.com/mkafrin/PolyZone/blob/master/ComboZone.lua) + + MIT License + + Copyright (c) 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 lastNearbyEntries = {} +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 +---@return GridEntry[] +function lib.grid.getNearbyEntries(point) + local minX, maxX, minY, maxY = getGridDimensions(point, xDelta, yDelta) + + if lastNearbyEntries.minX == minX and + lastNearbyEntries.maxX == maxX and + lastNearbyEntries.minY == minY and + lastNearbyEntries.maxY == maxY then + return lastNearbyEntries.entries + end + + local entries = {} + 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] then + n = n + 1 + entrySet[entry] = true + entries[n] = entry + end + end + end + end + end + + lastNearbyEntries.minX = minX + lastNearbyEntries.maxX = maxX + lastNearbyEntries.minY = minY + lastNearbyEntries.maxY = maxY + lastNearbyEntries.entries = entries + + 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 + 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 + + return success +end + +return lib.grid