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.
103 lines
3.6 KiB
103 lines
3.6 KiB
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
|
|
|