mirror of
https://github.com/ElCeejo/creatura.git
synced 2025-07-12 17:42:27 -04:00
Pathfinding Fixes
mob_meta.get_node_height: Moved to pathfinding, and made public (creatura.get_node_height). Now uses collision box rather than node box, if available, unless flag specifies not to. Fix for getting node height for nodes with connected node boxes pathfinding.get_ground_level: We now have one copy between pathfinding and mob_meta. Can check a larger range of nodes at different heights. Takes into account node height of both target node, and the height of the node we're coming from. pathfinding.get_neighbors: Get maximum distance mob can travel up/down from mob stepheight. Improved checking diagonals are clear. Improved checking vertical clearance, takes into account height of current and target node. Vertical clearance can cope with nodes that have a different collision and node boxes, like snow. Vertical clearance has a tiny height adjustment so a 2 node heigh entity can fit through a 2 node gap. Fixed bug where it was assumed a node was reachable if it's the end goal. methods.movement_theta_pathfind and movement_pathfind: Fixed bug that raised goal pos by 0.5 nodes.
This commit is contained in:
parent
23c61e0751
commit
35069011d6
3 changed files with 185 additions and 231 deletions
|
@ -166,7 +166,6 @@ end
|
||||||
|
|
||||||
local function movement_theta_pathfind(self, pos2, speed)
|
local function movement_theta_pathfind(self, pos2, speed)
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local goal = pos2
|
|
||||||
self._path = self._path or {}
|
self._path = self._path or {}
|
||||||
local temp_goal = self._movement_data.temp_goal
|
local temp_goal = self._movement_data.temp_goal
|
||||||
if not temp_goal
|
if not temp_goal
|
||||||
|
@ -182,7 +181,6 @@ local function movement_theta_pathfind(self, pos2, speed)
|
||||||
table.remove(self._path, 1)
|
table.remove(self._path, 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
goal.y = pos.y + 0.5
|
|
||||||
local dir = vector.direction(self.object:get_pos(), pos2)
|
local dir = vector.direction(self.object:get_pos(), pos2)
|
||||||
local tyaw = minetest.dir_to_yaw(dir)
|
local tyaw = minetest.dir_to_yaw(dir)
|
||||||
local turn_rate = self.turn_rate or 10
|
local turn_rate = self.turn_rate or 10
|
||||||
|
@ -201,7 +199,7 @@ local function movement_theta_pathfind(self, pos2, speed)
|
||||||
self:animate("walk")
|
self:animate("walk")
|
||||||
self:set_gravity(-9.8)
|
self:set_gravity(-9.8)
|
||||||
self:set_forward_velocity(speed or 2)
|
self:set_forward_velocity(speed or 2)
|
||||||
if self:pos_in_box(goal) then
|
if self:pos_in_box(pos2) then
|
||||||
self:halt()
|
self:halt()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -210,7 +208,6 @@ creatura.register_movement_method("creatura:theta_pathfind", movement_theta_path
|
||||||
|
|
||||||
local function movement_pathfind(self, pos2, speed)
|
local function movement_pathfind(self, pos2, speed)
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local goal = pos2
|
|
||||||
local temp_goal = self._movement_data.temp_goal
|
local temp_goal = self._movement_data.temp_goal
|
||||||
self._path = self._path or {}
|
self._path = self._path or {}
|
||||||
if (not temp_goal
|
if (not temp_goal
|
||||||
|
@ -227,7 +224,6 @@ local function movement_pathfind(self, pos2, speed)
|
||||||
table.remove(self._path, 1)
|
table.remove(self._path, 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
goal.y = pos.y + 0.5
|
|
||||||
local dir = vector.direction(self.object:get_pos(), pos2)
|
local dir = vector.direction(self.object:get_pos(), pos2)
|
||||||
local tyaw = minetest.dir_to_yaw(dir)
|
local tyaw = minetest.dir_to_yaw(dir)
|
||||||
local turn_rate = self.turn_rate or 10
|
local turn_rate = self.turn_rate or 10
|
||||||
|
|
82
mob_meta.lua
82
mob_meta.lua
|
@ -62,79 +62,15 @@ end
|
||||||
|
|
||||||
local default_node_def = {walkable = true} -- both ignore and unknown nodes are walkable
|
local default_node_def = {walkable = true} -- both ignore and unknown nodes are walkable
|
||||||
|
|
||||||
local function get_node_height(name)
|
|
||||||
local def = minetest.registered_nodes[name]
|
|
||||||
if not def then return 0.5 end
|
|
||||||
if def.walkable then
|
|
||||||
if def.drawtype == "nodebox" then
|
|
||||||
if def.node_box
|
|
||||||
and def.node_box.type == "fixed" then
|
|
||||||
if type(def.node_box.fixed[1]) == "number" then
|
|
||||||
return 0.5 + def.node_box.fixed[5]
|
|
||||||
elseif type(def.node_box.fixed[1]) == "table" then
|
|
||||||
return 0.5 + def.node_box.fixed[1][5]
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_node_def(name)
|
local function get_node_def(name)
|
||||||
local def = minetest.registered_nodes[name] or default_node_def
|
local def = minetest.registered_nodes[name] or default_node_def
|
||||||
if def.walkable
|
if def.walkable
|
||||||
and get_node_height(name) < 0.26 then
|
and creatura.get_node_height(name) < 0.26 then
|
||||||
def.walkable = false -- workaround for nodes like snow
|
def.walkable = false -- workaround for nodes like snow
|
||||||
end
|
end
|
||||||
return def
|
return def
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_ground_level(pos2, max_diff)
|
|
||||||
local node = minetest.get_node(pos2)
|
|
||||||
local node_under = minetest.get_node({
|
|
||||||
x = pos2.x,
|
|
||||||
y = pos2.y - 1,
|
|
||||||
z = pos2.z
|
|
||||||
})
|
|
||||||
local walkable = get_node_def(node_under.name) and not get_node_def(node.name)
|
|
||||||
if walkable then
|
|
||||||
return pos2
|
|
||||||
end
|
|
||||||
local diff = 0
|
|
||||||
if not get_node_def(node_under.name) then
|
|
||||||
for i = 1, max_diff do
|
|
||||||
pos2.y = pos2.y - 1
|
|
||||||
node = minetest.get_node(pos2)
|
|
||||||
node_under = minetest.get_node({
|
|
||||||
x = pos2.x,
|
|
||||||
y = pos2.y - 1,
|
|
||||||
z = pos2.z
|
|
||||||
})
|
|
||||||
walkable = get_node_def(node_under.name) and not get_node_def(node.name)
|
|
||||||
if walkable then break end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i = 1, max_diff do
|
|
||||||
pos2.y = pos2.y + 1
|
|
||||||
node = minetest.get_node(pos2)
|
|
||||||
node_under = minetest.get_node({
|
|
||||||
x = pos2.x,
|
|
||||||
y = pos2.y - 1,
|
|
||||||
z = pos2.z
|
|
||||||
})
|
|
||||||
walkable = get_node_def(node_under.name) and not get_node_def(node.name)
|
|
||||||
if walkable then break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return pos2
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
-- Physics/Vitals Tick --
|
-- Physics/Vitals Tick --
|
||||||
|
@ -247,12 +183,12 @@ function mob:turn_to(tyaw, rate)
|
||||||
|
|
||||||
yaw = yaw + pi
|
yaw = yaw + pi
|
||||||
tyaw = (tyaw + pi) % pi2
|
tyaw = (tyaw + pi) % pi2
|
||||||
|
|
||||||
local step = math.min(self.dtime * weight, abs(tyaw - yaw) % pi2)
|
local step = math.min(self.dtime * weight, abs(tyaw - yaw) % pi2)
|
||||||
|
|
||||||
local dir = abs(tyaw - yaw) > pi and -1 or 1
|
local dir = abs(tyaw - yaw) > pi and -1 or 1
|
||||||
dir = tyaw > yaw and dir * 1 or dir * -1
|
dir = tyaw > yaw and dir * 1 or dir * -1
|
||||||
|
|
||||||
local nyaw = (yaw + step * dir) % pi2
|
local nyaw = (yaw + step * dir) % pi2
|
||||||
self.object:set_yaw(nyaw - pi)
|
self.object:set_yaw(nyaw - pi)
|
||||||
self.last_yaw = self.object:get_yaw()
|
self.last_yaw = self.object:get_yaw()
|
||||||
|
@ -297,7 +233,7 @@ end
|
||||||
|
|
||||||
-- Punch 'target'
|
-- Punch 'target'
|
||||||
|
|
||||||
function mob:punch_target(target) --
|
function mob:punch_target(target) --
|
||||||
target:punch(self.object, 1.0, {
|
target:punch(self.object, 1.0, {
|
||||||
full_punch_interval = 1.0,
|
full_punch_interval = 1.0,
|
||||||
damage_groups = {fleshy = self.damage or 5},
|
damage_groups = {fleshy = self.damage or 5},
|
||||||
|
@ -371,7 +307,7 @@ function mob:get_wander_pos(min_range, max_range, dir)
|
||||||
local offset = vector.add(pos, vec_multi(vec_dir(pos, self.object:get_pos()), 1.5))
|
local offset = vector.add(pos, vec_multi(vec_dir(pos, self.object:get_pos()), 1.5))
|
||||||
pos.x = floor(offset.x + 0.5)
|
pos.x = floor(offset.x + 0.5)
|
||||||
pos.z = floor(offset.z + 0.5)
|
pos.z = floor(offset.z + 0.5)
|
||||||
pos = get_ground_level(pos, 1)
|
pos = creatura.get_ground_level(pos, 1, 1, 0)
|
||||||
end
|
end
|
||||||
local width = self.width
|
local width = self.width
|
||||||
local outset = random(min_range, max_range)
|
local outset = random(min_range, max_range)
|
||||||
|
@ -595,7 +531,7 @@ end
|
||||||
|
|
||||||
function mob:get_height()
|
function mob:get_height()
|
||||||
local hitbox = self:get_hitbox()
|
local hitbox = self:get_hitbox()
|
||||||
return hitbox[5] - hitbox[2]
|
return hitbox[5] - hitbox[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Return current visual size
|
-- Return current visual size
|
||||||
|
@ -628,7 +564,7 @@ function mob:follow_wielded_item(player)
|
||||||
and (is_value_in_table(self.follow, name)
|
and (is_value_in_table(self.follow, name)
|
||||||
or is_group_in_table(self.follow, name)) then
|
or is_group_in_table(self.follow, name)) then
|
||||||
return item, name
|
return item, name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob:get_target(target)
|
function mob:get_target(target)
|
||||||
|
@ -719,7 +655,7 @@ function mob:activate(staticdata, dtime)
|
||||||
-- Staticdata
|
-- Staticdata
|
||||||
if staticdata then
|
if staticdata then
|
||||||
local data = minetest.deserialize(staticdata)
|
local data = minetest.deserialize(staticdata)
|
||||||
if data then
|
if data then
|
||||||
for k, v in pairs(data) do
|
for k, v in pairs(data) do
|
||||||
self[k] = v
|
self[k] = v
|
||||||
end
|
end
|
||||||
|
|
328
pathfinder.lua
328
pathfinder.lua
|
@ -44,39 +44,55 @@ local function moveable(pos, width, height)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_ground_level(pos2, max_height)
|
creatura.get_node_height = function(name, force_node_box)
|
||||||
local node = minetest.get_node(pos2)
|
local def = minetest.registered_nodes[name]
|
||||||
local node_under = minetest.get_node({
|
if not def then return 0.5 end
|
||||||
x = pos2.x,
|
if def.walkable then
|
||||||
y = pos2.y - 1,
|
if def.drawtype == "nodebox" then
|
||||||
z = pos2.z
|
if def.collision_box and not force_node_box
|
||||||
})
|
and (def.collision_box.type == "fixed" or def.collision_box.type == "connected") then
|
||||||
local height = 0
|
if type(def.collision_box.fixed[1]) == "number" then
|
||||||
local walkable = is_node_walkable(node_under.name) and not is_node_walkable(node.name)
|
return 0.5 + def.collision_box.fixed[5]
|
||||||
if walkable then
|
elseif type(def.collision_box.fixed[1]) == "table" then
|
||||||
return pos2
|
return 0.5 + def.collision_box.fixed[1][5]
|
||||||
elseif not walkable then
|
else
|
||||||
if not is_node_walkable(node_under.name) then
|
return 1
|
||||||
while not is_node_walkable(node_under.name)
|
end
|
||||||
and height < max_height do
|
elseif def.node_box
|
||||||
pos2.y = pos2.y - 1
|
and (def.node_box.type == "fixed" or def.node_box.type == "connected") then
|
||||||
node_under = minetest.get_node({
|
if type(def.node_box.fixed[1]) == "number" then
|
||||||
x = pos2.x,
|
return 0.5 + def.node_box.fixed[5]
|
||||||
y = pos2.y - 1,
|
elseif type(def.node_box.fixed[1]) == "table" then
|
||||||
z = pos2.z
|
return 0.5 + def.node_box.fixed[1][5]
|
||||||
})
|
else
|
||||||
height = height + 1
|
return 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
while is_node_walkable(node.name)
|
return 1
|
||||||
and height < max_height do
|
end
|
||||||
pos2.y = pos2.y + 1
|
else
|
||||||
node = minetest.get_node(pos2)
|
return 1
|
||||||
height = height + 1
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
creatura.get_ground_level = function(pos, max_up, max_down, current_node_height)
|
||||||
|
for y = math.ceil(max_up) + 1, -(math.ceil(max_down)) - 1, -1 do
|
||||||
|
local pos2 = vector.new(pos.x, pos.y + y, pos.z)
|
||||||
|
local node = minetest.get_node(pos2)
|
||||||
|
local node_under = minetest.get_node(pos2 + vector.new(0, -1, 0))
|
||||||
|
|
||||||
|
if not is_node_walkable(node.name) and is_node_walkable(node_under.name) then
|
||||||
|
local node_height = creatura.get_node_height(node_under.name)
|
||||||
|
local y_diff = y - 1 + node_height - current_node_height
|
||||||
|
if y_diff <= max_up and y_diff >= (-max_down) then
|
||||||
|
return pos2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return pos2
|
|
||||||
end
|
end
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_distance(start_pos, end_pos)
|
local function get_distance(start_pos, end_pos)
|
||||||
|
@ -120,6 +136,99 @@ end
|
||||||
|
|
||||||
-- Find a path from start to goal
|
-- Find a path from start to goal
|
||||||
|
|
||||||
|
local function get_neighbors(self, pos, goal, swim, fly, climb, tbl, open, closed)
|
||||||
|
local width = self.width
|
||||||
|
local height = self.height
|
||||||
|
local result = {}
|
||||||
|
local max_up = self.stepheight or 1
|
||||||
|
local max_down = self.stepheight or 1
|
||||||
|
|
||||||
|
local node_name = minetest.get_node(pos).name
|
||||||
|
-- Get the height of the node collision box (and of its node box, if different)
|
||||||
|
local node_height = 0
|
||||||
|
local node_height_node_box = 0
|
||||||
|
if is_node_walkable(node_name) then
|
||||||
|
node_height = creatura.get_node_height(node_name)
|
||||||
|
node_height_node_box = creatura.get_node_height(node_name, true)
|
||||||
|
else
|
||||||
|
node_height = creatura.get_node_height(minetest.get_node(pos + vector.new(0, -1, 0)).name) - 1
|
||||||
|
node_height_node_box = creatura.get_node_height(minetest.get_node(pos + vector.new(0, -1, 0)).name, true) - 1
|
||||||
|
end
|
||||||
|
-- Calculate the height difference between the collision and node boxes
|
||||||
|
-- (This is because the mob will be standing on the collision box, but the
|
||||||
|
-- raycast checks will collide with the node box, so we must avoid it)
|
||||||
|
local node_height_diff = node_height_node_box - node_height
|
||||||
|
|
||||||
|
for i = 1, #tbl do
|
||||||
|
local neighbor = vector.add(pos, tbl[i])
|
||||||
|
if not open[minetest.hash_node_position(neighbor)]
|
||||||
|
and not closed[minetest.hash_node_position(neighbor)] then
|
||||||
|
|
||||||
|
local neighbor_x
|
||||||
|
local neighbor_z
|
||||||
|
|
||||||
|
if tbl[i].y == 0
|
||||||
|
and not fly
|
||||||
|
and not swim then
|
||||||
|
neighbor = creatura.get_ground_level(neighbor, max_up, max_down, node_height)
|
||||||
|
if neighbor and tbl[i].x ~= 0 and tbl[i].z ~= 0 then
|
||||||
|
-- This is a diagonal, check both corners are clear and same Y
|
||||||
|
neighbor_x = creatura.get_ground_level(vector.new(neighbor.x, neighbor.y, pos.z), max_up, max_down, node_height)
|
||||||
|
neighbor_z = creatura.get_ground_level(vector.new(pos.x, neighbor.y, neighbor.z), max_up, max_down, node_height)
|
||||||
|
if not neighbor_x or not neighbor_z
|
||||||
|
or neighbor_x.y ~= neighbor.y
|
||||||
|
or neighbor_z.y ~= neighbor.y then
|
||||||
|
neighbor = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if neighbor then
|
||||||
|
local can_move = true
|
||||||
|
if swim then
|
||||||
|
local neighbor_node = minetest.get_node(neighbor)
|
||||||
|
can_move = is_node_liquid(neighbor_node.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Adjust entity Y in clearance check by this much
|
||||||
|
local y_adjustment = -0.49
|
||||||
|
-- Adjust entity height in clearance check by this much
|
||||||
|
local h_adjustment = -0.02
|
||||||
|
-- Get the height of the node collision box, and the difference to the node box
|
||||||
|
local neighbor_height = creatura.get_node_height(minetest.get_node(neighbor + vector.new(0, -1, 0)).name) - 1
|
||||||
|
local neighbor_height_node_box = creatura.get_node_height(minetest.get_node(neighbor + vector.new(0, -1, 0)).name, true) - 1
|
||||||
|
local neighbor_height_diff = neighbor_height_node_box - neighbor_height
|
||||||
|
-- Check there is enough vertical clearance to move to this node
|
||||||
|
local height_clearance = math.max(pos.y + node_height - neighbor.y - neighbor_height, 0)
|
||||||
|
if not moveable(vec_raise(neighbor, y_adjustment + neighbor_height + neighbor_height_diff), width, height + h_adjustment + height_clearance - neighbor_height_diff) then
|
||||||
|
can_move = false
|
||||||
|
end
|
||||||
|
if tbl[i].x ~= 0 and tbl[i].z ~= 0 then
|
||||||
|
-- If target node is diagonal, check the orthogonal nodes too
|
||||||
|
if not moveable(vec_raise(neighbor_x, y_adjustment + neighbor_height + neighbor_height_diff), width, height + h_adjustment + height_clearance + neighbor_height_diff)
|
||||||
|
or not moveable(vec_raise(neighbor_z, y_adjustment + neighbor_height + neighbor_height_diff), width, height + h_adjustment + height_clearance + neighbor_height_diff) then
|
||||||
|
can_move = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- If we're going upwards, check there's enough clearance above our head
|
||||||
|
height_clearance = math.max(neighbor.y + neighbor_height - pos.y - node_height, 0)
|
||||||
|
if height_clearance > 0 and not moveable(vec_raise(pos, y_adjustment + node_height + node_height_diff), width, height + h_adjustment + height_clearance - node_height_diff) then
|
||||||
|
can_move = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if (can_move
|
||||||
|
or (climb
|
||||||
|
and neighbor.x == pos.x
|
||||||
|
and neighbor.z == pos.z))
|
||||||
|
and (not swim
|
||||||
|
or is_node_liquid(minetest.get_node(neighbor).name)) then
|
||||||
|
table.insert(result, neighbor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
function creatura.find_path(self, start, goal, obj_width, obj_height, max_open, climb, fly, swim)
|
function creatura.find_path(self, start, goal, obj_width, obj_height, max_open, climb, fly, swim)
|
||||||
climb = climb or false
|
climb = climb or false
|
||||||
fly = fly or false
|
fly = fly or false
|
||||||
|
@ -158,49 +267,7 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_neighbors(pos, width, height, tbl, open, closed)
|
local function find_path(self, start, goal)
|
||||||
local result = {}
|
|
||||||
for i = 1, #tbl do
|
|
||||||
local neighbor = vector.add(pos, tbl[i])
|
|
||||||
if neighbor.y == pos.y
|
|
||||||
and not fly
|
|
||||||
and not swim then
|
|
||||||
neighbor = get_ground_level(neighbor, 1)
|
|
||||||
end
|
|
||||||
local can_move = get_line_of_sight({x = pos.x, y = neighbor.y, z = pos.z}, neighbor)
|
|
||||||
if swim then
|
|
||||||
can_move = true
|
|
||||||
end
|
|
||||||
if not moveable(vec_raise(neighbor, -0.49), width, height) then
|
|
||||||
can_move = false
|
|
||||||
if neighbor.y == pos.y
|
|
||||||
and moveable(vec_raise(neighbor, 0.51), width, height) then
|
|
||||||
neighbor = vec_raise(neighbor, 1)
|
|
||||||
can_move = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if vector.equals(neighbor, goal) then
|
|
||||||
can_move = true
|
|
||||||
end
|
|
||||||
if open[minetest.hash_node_position(neighbor)]
|
|
||||||
or closed[minetest.hash_node_position(neighbor)] then
|
|
||||||
can_move = false
|
|
||||||
end
|
|
||||||
if can_move
|
|
||||||
and ((is_on_ground(neighbor)
|
|
||||||
or (fly or swim))
|
|
||||||
or (neighbor.x == pos.x
|
|
||||||
and neighbor.z == pos.z
|
|
||||||
and climb))
|
|
||||||
and (not swim
|
|
||||||
or is_node_liquid(minetest.get_node(neighbor).name)) then
|
|
||||||
table.insert(result, neighbor)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_path(start, goal)
|
|
||||||
local us_time = minetest.get_us_time()
|
local us_time = minetest.get_us_time()
|
||||||
|
|
||||||
start = {
|
start = {
|
||||||
|
@ -208,7 +275,7 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
y = floor(start.y + 0.5),
|
y = floor(start.y + 0.5),
|
||||||
z = floor(start.z + 0.5)
|
z = floor(start.z + 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
goal = {
|
goal = {
|
||||||
x = floor(goal.x + 0.5),
|
x = floor(goal.x + 0.5),
|
||||||
y = floor(goal.y + 0.5),
|
y = floor(goal.y + 0.5),
|
||||||
|
@ -219,22 +286,22 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
and goal.z == start.z then -- No path can be found
|
and goal.z == start.z then -- No path can be found
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local openSet = self._path_data.open or {}
|
local openSet = self._path_data.open or {}
|
||||||
|
|
||||||
local closedSet = self._path_data.closed or {}
|
local closedSet = self._path_data.closed or {}
|
||||||
|
|
||||||
local start_index = minetest.hash_node_position(start)
|
local start_index = minetest.hash_node_position(start)
|
||||||
|
|
||||||
openSet[start_index] = {
|
openSet[start_index] = {
|
||||||
pos = start,
|
pos = start,
|
||||||
parent = nil,
|
parent = nil,
|
||||||
gScore = 0,
|
gScore = 0,
|
||||||
fScore = get_distance(start, goal)
|
fScore = get_distance(start, goal)
|
||||||
}
|
}
|
||||||
|
|
||||||
local count = self._path_data.count or 1
|
local count = self._path_data.count or 1
|
||||||
|
|
||||||
while count > 0 do
|
while count > 0 do
|
||||||
if minetest.get_us_time() - us_time > a_star_alloted_time then
|
if minetest.get_us_time() - us_time > a_star_alloted_time then
|
||||||
self._path_data = {
|
self._path_data = {
|
||||||
|
@ -248,14 +315,14 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
-- Initialize ID and data
|
-- Initialize ID and data
|
||||||
local current_id
|
local current_id
|
||||||
local current
|
local current
|
||||||
|
|
||||||
-- Get an initial id in open set
|
-- Get an initial id in open set
|
||||||
for i, v in pairs(openSet) do
|
for i, v in pairs(openSet) do
|
||||||
current_id = i
|
current_id = i
|
||||||
current = v
|
current = v
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Find lowest f cost
|
-- Find lowest f cost
|
||||||
for i, v in pairs(openSet) do
|
for i, v in pairs(openSet) do
|
||||||
if v.fScore < current.fScore then
|
if v.fScore < current.fScore then
|
||||||
|
@ -263,7 +330,7 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
current = v
|
current = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add lowest fScore to closedSet and remove from openSet
|
-- Add lowest fScore to closedSet and remove from openSet
|
||||||
openSet[current_id] = nil
|
openSet[current_id] = nil
|
||||||
closedSet[current_id] = current
|
closedSet[current_id] = current
|
||||||
|
@ -296,11 +363,11 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
self._path_data = {}
|
self._path_data = {}
|
||||||
return reverse_path
|
return reverse_path
|
||||||
end
|
end
|
||||||
|
|
||||||
count = count - 1
|
count = count - 1
|
||||||
|
|
||||||
local adjacent = get_neighbors(current.pos, obj_width, obj_height, path_neighbors, openSet, closedSet)
|
local adjacent = get_neighbors(self, current.pos, goal, swim, fly, climb, path_neighbors, openSet, closedSet)
|
||||||
|
|
||||||
-- Go through neighboring nodes
|
-- Go through neighboring nodes
|
||||||
for i = 1, #adjacent do
|
for i = 1, #adjacent do
|
||||||
local neighbor = {
|
local neighbor = {
|
||||||
|
@ -309,21 +376,18 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
gScore = 0,
|
gScore = 0,
|
||||||
fScore = 0
|
fScore = 0
|
||||||
}
|
}
|
||||||
local temp_gScore = current.gScore + get_distance_to_neighbor(current.pos, neighbor.pos)
|
local neighbor_id = minetest.hash_node_position(neighbor.pos)
|
||||||
local new_gScore = 0
|
local neighbour_gScore = current.gScore + get_distance_to_neighbor(current.pos, neighbor.pos)
|
||||||
if openSet[minetest.hash_node_position(neighbor.pos)] then
|
if (not openSet[neighbor_id]
|
||||||
new_gScore = openSet[minetest.hash_node_position(neighbor.pos)].gScore
|
or neighbour_gScore < openSet[neighbor_id].gScore)
|
||||||
end
|
and not closedSet[neighbor_id] then
|
||||||
if (temp_gScore < new_gScore
|
if not openSet[neighbor_id] then
|
||||||
or not openSet[minetest.hash_node_position(neighbor.pos)])
|
|
||||||
and not closedSet[minetest.hash_node_position(neighbor.pos)] then
|
|
||||||
if not openSet[minetest.hash_node_position(neighbor.pos)] then
|
|
||||||
count = count + 1
|
count = count + 1
|
||||||
end
|
end
|
||||||
local hCost = get_distance_to_neighbor(neighbor.pos, goal)
|
local hCost = get_distance_to_neighbor(neighbor.pos, goal)
|
||||||
neighbor.gScore = temp_gScore
|
neighbor.gScore = neighbour_gScore
|
||||||
neighbor.fScore = temp_gScore + hCost
|
neighbor.fScore = neighbour_gScore + hCost
|
||||||
openSet[minetest.hash_node_position(neighbor.pos)] = neighbor
|
openSet[neighbor_id] = neighbor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if count > (max_open or 100) then
|
if count > (max_open or 100) then
|
||||||
|
@ -334,7 +398,7 @@ function creatura.find_path(self, start, goal, obj_width, obj_height, max_open,
|
||||||
self._path_data = {}
|
self._path_data = {}
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return find_path(start, goal)
|
return find_path(self, start, goal)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,49 +472,7 @@ function creatura.find_theta_path(self, start, goal, obj_width, obj_height, max_
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_neighbors(pos, width, height, tbl, open, closed)
|
local function find_path(self, start, goal)
|
||||||
local result = {}
|
|
||||||
for i = 1, #tbl do
|
|
||||||
local neighbor = vector.add(pos, tbl[i])
|
|
||||||
if neighbor.y == pos.y
|
|
||||||
and not fly
|
|
||||||
and not swim then
|
|
||||||
neighbor = get_ground_level(neighbor, 1)
|
|
||||||
end
|
|
||||||
local can_move = get_line_of_sight({x = pos.x, y = neighbor.y, z = pos.z}, neighbor)
|
|
||||||
if swim then
|
|
||||||
can_move = true
|
|
||||||
end
|
|
||||||
if not moveable(vec_raise(neighbor, -0.49), width, height) then
|
|
||||||
can_move = false
|
|
||||||
if neighbor.y == pos.y
|
|
||||||
and moveable(vec_raise(neighbor, 0.51), width, height) then
|
|
||||||
neighbor = vec_raise(neighbor, 1)
|
|
||||||
can_move = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if vector.equals(neighbor, goal) then
|
|
||||||
can_move = true
|
|
||||||
end
|
|
||||||
if open[minetest.hash_node_position(neighbor)]
|
|
||||||
or closed[minetest.hash_node_position(neighbor)] then
|
|
||||||
can_move = false
|
|
||||||
end
|
|
||||||
if can_move
|
|
||||||
and ((is_on_ground(neighbor)
|
|
||||||
or (fly or swim))
|
|
||||||
or (neighbor.x == pos.x
|
|
||||||
and neighbor.z == pos.z
|
|
||||||
and climb))
|
|
||||||
and (not swim
|
|
||||||
or is_node_liquid(minetest.get_node(neighbor).name)) then
|
|
||||||
table.insert(result, neighbor)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_path(start, goal)
|
|
||||||
local us_time = minetest.get_us_time()
|
local us_time = minetest.get_us_time()
|
||||||
|
|
||||||
start = {
|
start = {
|
||||||
|
@ -458,7 +480,7 @@ function creatura.find_theta_path(self, start, goal, obj_width, obj_height, max_
|
||||||
y = floor(start.y + 0.5),
|
y = floor(start.y + 0.5),
|
||||||
z = floor(start.z + 0.5)
|
z = floor(start.z + 0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
goal = {
|
goal = {
|
||||||
x = floor(goal.x + 0.5),
|
x = floor(goal.x + 0.5),
|
||||||
y = floor(goal.y + 0.5),
|
y = floor(goal.y + 0.5),
|
||||||
|
@ -469,22 +491,22 @@ function creatura.find_theta_path(self, start, goal, obj_width, obj_height, max_
|
||||||
and goal.z == start.z then -- No path can be found
|
and goal.z == start.z then -- No path can be found
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local openSet = self._path_data.open or {}
|
local openSet = self._path_data.open or {}
|
||||||
|
|
||||||
local closedSet = self._path_data.closed or {}
|
local closedSet = self._path_data.closed or {}
|
||||||
|
|
||||||
local start_index = minetest.hash_node_position(start)
|
local start_index = minetest.hash_node_position(start)
|
||||||
|
|
||||||
openSet[start_index] = {
|
openSet[start_index] = {
|
||||||
pos = start,
|
pos = start,
|
||||||
parent = nil,
|
parent = nil,
|
||||||
gScore = 0,
|
gScore = 0,
|
||||||
fScore = get_distance(start, goal)
|
fScore = get_distance(start, goal)
|
||||||
}
|
}
|
||||||
|
|
||||||
local count = self._path_data.count or 1
|
local count = self._path_data.count or 1
|
||||||
|
|
||||||
while count > 0 do
|
while count > 0 do
|
||||||
if minetest.get_us_time() - us_time > theta_star_alloted_time then
|
if minetest.get_us_time() - us_time > theta_star_alloted_time then
|
||||||
self._path_data = {
|
self._path_data = {
|
||||||
|
@ -542,11 +564,11 @@ function creatura.find_theta_path(self, start, goal, obj_width, obj_height, max_
|
||||||
self._path_data = {}
|
self._path_data = {}
|
||||||
return reverse_path
|
return reverse_path
|
||||||
end
|
end
|
||||||
|
|
||||||
count = count - 1
|
count = count - 1
|
||||||
|
|
||||||
local adjacent = get_neighbors(current.pos, obj_width, obj_height, path_neighbors, openSet, closedSet)
|
local adjacent = get_neighbors(self, current.pos, goal, swim, fly, climb, path_neighbors, openSet, closedSet)
|
||||||
|
|
||||||
-- Go through neighboring nodes
|
-- Go through neighboring nodes
|
||||||
for i = 1, #adjacent do
|
for i = 1, #adjacent do
|
||||||
local neighbor = {
|
local neighbor = {
|
||||||
|
@ -606,5 +628,5 @@ function creatura.find_theta_path(self, start, goal, obj_width, obj_height, max_
|
||||||
self._path_data = {}
|
self._path_data = {}
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return find_path(start, goal)
|
return find_path(self, start, goal)
|
||||||
end
|
end
|
Loading…
Add table
Add a link
Reference in a new issue