91 lines
3.0 KiB
Lua
91 lines
3.0 KiB
Lua
local walkable, cost = {}, {}
|
|
minetest.register_on_mods_loaded(function()
|
|
for k, v in pairs(minetest.registered_nodes) do
|
|
if v.walkable then
|
|
walkable[minetest.get_content_id(k)] = true
|
|
end
|
|
if v.groups.pathable and v.groups.pathable > 0 then
|
|
cost[minetest.get_content_id(k)] = v.groups.pathable
|
|
end
|
|
end
|
|
end)
|
|
|
|
local function pathfind(origin, target, radius)
|
|
origin, target = vector.round(origin), vector.round(target)
|
|
target.y = 0 -- TODO: Jumping/Climbing
|
|
|
|
local padding = vector.new(radius, 1, radius)
|
|
local padded_min, padded_max = vector.sort(origin, target)
|
|
padded_min, padded_max = vector.subtract(padded_min, padding), vector.add(padded_max, padding)
|
|
|
|
local VM = minetest.get_voxel_manip()
|
|
local minp, maxp = VM:read_from_map(padded_min, padded_max)
|
|
local VA, node_data = VoxelArea:new{MinEdge = minp, MaxEdge = maxp}, VM:get_data()
|
|
|
|
local x_width, y_width, z_width = maxp.x - minp.x, maxp.y - minp.y, maxp.z - minp.z
|
|
|
|
local function get_neighbors(i)
|
|
-- Bounds check Y - 1
|
|
if math.floor(((i - 1 - VA.ystride) % VA.zstride) / VA.ystride) < 0 then
|
|
return {}
|
|
end
|
|
|
|
local north = math.floor((i - 1 + VA.zstride) / VA.zstride) <= z_width
|
|
local south = math.floor((i - 1 - VA.zstride) / VA.zstride) >= 0
|
|
local east = math.floor((i - 1 + 1) % VA.zstride % VA.ystride) <= x_width
|
|
local west = math.floor((i - 1 - 1) % VA.zstride % VA.ystride) >= 0
|
|
|
|
local neighbors = {}
|
|
if north then
|
|
neighbors[#neighbors + 1] = i + VA.zstride
|
|
if east then neighbors[#neighbors + 1] = i + VA.zstride + 1 end
|
|
if west then neighbors[#neighbors + 1] = i + VA.zstride - 1 end
|
|
end
|
|
if south then
|
|
neighbors[#neighbors + 1] = i - VA.zstride
|
|
if east then neighbors[#neighbors + 1] = i - VA.zstride + 1 end
|
|
if west then neighbors[#neighbors + 1] = i - VA.zstride - 1 end
|
|
end
|
|
if east then neighbors[#neighbors + 1] = i + 1 end
|
|
if west then neighbors[#neighbors + 1] = i - 1 end
|
|
|
|
return neighbors
|
|
end
|
|
|
|
local function is_good_node(i)
|
|
return (not walkable[node_data[i]]) and walkable[node_data[i - VA.ystride]]
|
|
end
|
|
|
|
local function get_cost(a, b)
|
|
return cost[node_data[b]] or 1
|
|
end
|
|
|
|
local function get_heuristic(a, b)
|
|
local a_pos, b_pos = VA:position(a), VA:position(b)
|
|
return vector.distance(a_pos, b_pos)
|
|
end
|
|
|
|
local raw_path = util.astar(VA:indexp(origin), VA:indexp(target), get_neighbors, is_good_node, get_cost, get_heuristic)
|
|
if not raw_path then return false end
|
|
|
|
table.remove(raw_path, 1) -- No need to walk to where we already are
|
|
local path = {}
|
|
for _, node_index in ipairs(raw_path) do
|
|
table.insert(path, VA:position(node_index))
|
|
end
|
|
|
|
local trimmed_path, old_direction, old_pos = {}, false, table.remove(path, 1)
|
|
for _, pos in ipairs(path) do
|
|
local direction = vector.direction(pos, old_pos)
|
|
if not old_direction or not vector.equals(direction, old_direction) then
|
|
table.insert(trimmed_path, old_pos)
|
|
old_pos, old_direction = pos, direction
|
|
end
|
|
end
|
|
table.insert(trimmed_path, table.remove(path))
|
|
|
|
return trimmed_path
|
|
end
|
|
|
|
return pathfind
|