You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
3.6 KiB
104 lines
3.6 KiB
1 year ago
|
local util = {}
|
||
|
|
||
|
local AXES = {
|
||
|
x = vector.new(1, 0, 0),
|
||
|
y = vector.new(0, 1, 0),
|
||
|
z = vector.new(0, 0, 1),
|
||
|
}
|
||
|
util.AXES = AXES;
|
||
|
|
||
|
-- Gets an array of vectors relative to `pos` of neighbors that have the specified `match`.
|
||
|
local function find_neighbors_with_name(pos, match)
|
||
|
local result = {}
|
||
|
for _, v in pairs(AXES) do
|
||
|
if minetest.get_node(pos - v).name == match then
|
||
|
table.insert(result, -v)
|
||
|
end
|
||
|
end
|
||
|
for _, v in pairs(AXES) do
|
||
|
if minetest.get_node(pos + v).name == match then
|
||
|
table.insert(result, v)
|
||
|
end
|
||
|
end
|
||
|
return result
|
||
|
end
|
||
|
util.find_neighbors_with_name = find_neighbors_with_name;
|
||
|
|
||
|
-- Moves `pos` towards `dir` while node is still `match`.
|
||
|
-- returns (vector, array) or nil
|
||
|
local function move_until_corner(pos, match, dir)
|
||
|
repeat pos = pos + dir
|
||
|
until minetest.get_node(pos + dir).name ~= match
|
||
|
local neighbors = find_neighbors_with_name(pos, match)
|
||
|
if #neighbors == 2
|
||
|
then return pos, neighbors
|
||
|
else return nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Moves `pos` towards `dir`, checking that each node has exactly two neighbors
|
||
|
-- that of type `match`, until a corner turning towards `expected_corner_dir` is hit.
|
||
|
-- returns vector or nil
|
||
|
local function move_until_corner_with_checks(pos, match, dir, expected_corner_dir)
|
||
|
while true do
|
||
|
pos = pos + dir
|
||
|
|
||
|
local neighbors = find_neighbors_with_name(pos, match)
|
||
|
if #neighbors ~= 2 then return nil end
|
||
|
|
||
|
-- Get the "next direction" the piece is going.
|
||
|
local next_dir = neighbors[1]
|
||
|
if next_dir == -dir then next_dir = neighbors[2] end
|
||
|
|
||
|
if next_dir ~= dir then
|
||
|
if next_dir == expected_corner_dir
|
||
|
then return pos
|
||
|
else return nil
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Finds a rectangle structure made from the specified node `match`.
|
||
|
-- returns { min = vector, max = vector } or nil
|
||
|
local function find_rectangle(pos, match)
|
||
|
local neighbors = find_neighbors_with_name(pos, match)
|
||
|
if #neighbors ~= 2 then return nil end
|
||
|
|
||
|
-- If we found a line piece (neighbors are opposite) then move in the
|
||
|
-- direction of the smallest xyz coordinate.
|
||
|
if neighbors[1] == -neighbors[2] then
|
||
|
-- NOTE: Due to how `get_neighbors_with_name` is written, the first element
|
||
|
-- is guaranteed to contain the vector pointing towards negative.
|
||
|
pos, neighbors = move_until_corner(pos, match, neighbors[1])
|
||
|
if not pos then return nil end
|
||
|
end
|
||
|
|
||
|
-- We're now guaranteed to be at a corner piece.
|
||
|
-- Move to the corner piece with the smallest xyz coordinate.
|
||
|
while neighbors[1].x + neighbors[1].y + neighbors[1].z < 1 do
|
||
|
pos, neighbors = move_until_corner(pos, match, neighbors[1])
|
||
|
if not pos then return nil end
|
||
|
end
|
||
|
|
||
|
-- Find the neighboring corners of the starting corner.
|
||
|
local first_corner = move_until_corner_with_checks(pos, match, neighbors[1], neighbors[2])
|
||
|
if not first_corner then return nil end
|
||
|
local second_corner = move_until_corner_with_checks(pos, match, neighbors[2], neighbors[1])
|
||
|
if not second_corner then return nil end
|
||
|
|
||
|
-- Continue towards the final corner from the new corners.
|
||
|
first_corner = move_until_corner_with_checks(first_corner, match, neighbors[2], -neighbors[1])
|
||
|
if not first_corner then return nil end
|
||
|
second_corner = move_until_corner_with_checks(second_corner, match, neighbors[1], -neighbors[2])
|
||
|
if not second_corner then return nil end
|
||
|
|
||
|
-- Ensure that they have met up at the same location.
|
||
|
if first_corner ~= second_corner then return nil end
|
||
|
|
||
|
return { min = pos, max = second_corner }
|
||
|
end
|
||
|
util.find_rectangle = find_rectangle;
|
||
|
|
||
|
return util
|