diff --git a/actions/pathfinding.lua b/actions/jumper.lua
similarity index 90%
rename from actions/pathfinding.lua
rename to actions/jumper.lua
index 3fa56e5..90c1087 100644
--- a/actions/pathfinding.lua
+++ b/actions/jumper.lua
@@ -1,20 +1,51 @@
--- Code based on Jumper library
+---------------------------------------------------------------------------------------
+-- This code is entirely based on the Jumper library by Roland Yonaba.
+-- The modifications are only to make it work under Minetest's secure
+-- environment. Therefore, the code in this file is under the MIT license
+-- as the original Jumper library. The original library code can be found
+-- here: https://github.com/Yonaba/Jumper
---- The Pathfinder class
+-- Modifications are by Hector Franqui (Zorman2000)
--- Modification by Zorman2000:
--- This file has been modified to be usable with Minetest.
--- Do to Minetest's mod security, the global "require()" call
--- is disabled, and needs to be replaced by one using Minetest's
--- environment.
--- - Get Minetest's insecure environment
--- - Change all "require" calls to "ie.require()"
---
--- The rest of the code is left intact and is (c) 2012-2013 Roland Yonaba
+---------------------------------------------------------------------------------------
+-- Copyright (c) 2012-2013 Roland Yonaba
+-- 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 variables declarations
local abs = math.abs
local sqrt = math.sqrt
+local max = math.max
+local floor = math.floor
+local t_insert, t_remove = table.insert, table.remove
+local huge = math.huge
+
+---------------------------------------------------------------------------------------
+-- Heuristics based on implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/core/heuristics.lua
+---------------------------------------------------------------------------------------
local Heuristics = {
['MANHATTAN'] = function(nodeA, nodeB)
local dx = abs(nodeA._x - nodeB._x)
@@ -28,6 +59,11 @@ local Heuristics = {
end
}
+
+---------------------------------------------------------------------------------------
+-- Node class implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/core/node.lua
+---------------------------------------------------------------------------------------
local Node = setmetatable({},
{__call = function(self,...)
return Node:new(...)
@@ -106,14 +142,11 @@ local Node = setmetatable({},
return self
end
----------------------------------------------------------------
--- Path class implementation by Ronald Yonaba
--- Original code here:
----------------------------------------------------------------
--- Local references
-local abs, max = math.abs, math.max
-local t_insert, t_remove = table.insert, table.remove
+---------------------------------------------------------------------------------------
+-- Path class implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/core/path.lua
+---------------------------------------------------------------------------------------
--- The `Path` class.
-- This class is callable.
-- Therefore, Path(...)
acts as a shortcut to Path:new(...)
.
@@ -294,12 +327,12 @@ function Path:append(p)
for node in p:nodes() do self:addNode(node) end
return self
end
-
- -- Local reference
-local floor = math.floor
-
+---------------------------------------------------------------------------------------
+-- Utils class based on implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/core/utils.lua
+---------------------------------------------------------------------------------------
local Utils = {
traceBackPath = function(finder, node, startNode)
local path = Path:new()
@@ -358,11 +391,11 @@ local Utils = {
end
}
----------------------------------------------------------------
--- B-Heap implementation by Ronald Yonaba
--- Original code here:
----------------------------------------------------------------
+---------------------------------------------------------------------------------------
+-- Bheap class implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/core/bheap.lua
+---------------------------------------------------------------------------------------
-- Default comparison function
local function f_min(a,b) return a < b end
@@ -507,15 +540,15 @@ function heap:heapify(item)
return self
end
----------------------------------------------------------------
--- Grid implementation by Ronald Yonaba
--- Original code here:
----------------------------------------------------------------
+
+---------------------------------------------------------------------------------------
+-- Grid class implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/grid.lua
+---------------------------------------------------------------------------------------
local pairs = pairs
local assert = assert
local next = next
local setmetatable = setmetatable
-local floor = math.floor
local coroutine = coroutine
-- Offsets for straights moves
@@ -904,13 +937,12 @@ function PostProcessGrid:getNodeAt(x,y)
end
----------------------------------------------------------------
--- A* algorithm implementation by Ronald Yonaba
--- Original code here:
----------------------------------------------------------------
+---------------------------------------------------------------------------------------
+-- A* algorithm based on implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/search/astar.lua
+---------------------------------------------------------------------------------------
-- Internalization
local ipairs = ipairs
-local huge = math.huge
-- Updates G-cost
local function computeCost(node, neighbour, finder, clearance)
@@ -973,6 +1005,11 @@ local function ASTAR(finder, startNode, endNode, clearance, toClear, overrideHeu
return nil
end
+
+---------------------------------------------------------------------------------------
+-- Pathfinder class based on implementation by Ronald Yonaba
+-- Original code here: https://github.com/Yonaba/Jumper/jumper/pathfinder.lua
+---------------------------------------------------------------------------------------
local Finders = {
['ASTAR'] = ASTAR
}
diff --git a/actions/pathfinder2.lua b/actions/pathfinder2.lua
deleted file mode 100644
index 593ab57..0000000
--- a/actions/pathfinder2.lua
+++ /dev/null
@@ -1,274 +0,0 @@
--- Pathfinder by Zorman2000
--- Pathfinding code with included A* implementation, customized
--- for Minetest. At the moment, paths can only be found in flat
--- terrain (only 2D pathfinding)
-
--- Public namespace
-pathfinder = {}
--- Private namespace for pathfinder functions
-local finder = {}
-
-pathfinder.node_types = {
- start = 0,
- goal = 1,
- walkable = 2,
- openable = 3,
- non_walkable = 4
-}
-
-pathfinder.nodes = {
- openable_prefix = {
- "doors:",
- "cottages:gate",
- "cottages:half_door"
- }
-}
-
-function pathfinder.find_path(start_pos, end_pos, extra_range, walkable_nodes)
- -- Create map
- local map = finder.create_map(start_pos, end_pos, extra_range, walkable_nodes)
- minetest.log("Number of nodes in map: "..dump(#map))
- -- Use A* algorithm
- local path = minetest.find_path(start_pos, end_pos, 30, 1, 1, "Dijkstra")
- minetest.log("Path: "..dump(path))
- return path
- --return finder.astar({name="air", pos=start_pos}, {name="air", pos=end_pos}, map)
-end
-
--- This function is used to determine if a node is walkable
--- or openable, in which case is good to use when finding a path
-function finder.is_good_node(node, exceptions)
- -- Is openable is to support doors, fence gates and other
- -- doors from other mods. Currently, default doors, gates
- -- and cottages doors are supported.
- local is_openable = false
- for _,node_prefix in pairs(pathfinder.nodes.openable_prefix) do
- local start_i,end_i = string.find(node.name, node_prefix)
- if start_i ~= nil then
- is_openable = true
- break
- end
- end
- if node ~= nil and node.name ~= nil and not minetest.registered_nodes[node.name].walkable then
- return pathfinder.node_types.walkable
- elseif is_openable then
- return pathfinder.node_types.openable
- else
- for i = 1, #exceptions do
- if node.name == exceptions[i] then
- return pathfinder.node_types.walkable
- end
- end
- return pathfinder.node_types.non_walkable
- end
-end
-
--- Maps a 2D slice of Minetest terrain into an array of nodes
--- Extra range is a number that will be added in the x and the z coordinates
--- to allow more room to find paths.
--- Walkables is an array of node names which are considered walkable,
--- even if they are not.
-function finder.create_map(start_pos, end_pos, extra_range, walkables)
-
- minetest.log("Start pos: "..minetest.pos_to_string(start_pos))
- minetest.log("End pos: "..minetest.pos_to_string(end_pos))
-
- -- Calculate all signs to ensure:
- -- 1. Correct area calculation
- -- 2. Iterate in the correct direction
- local start_x_sign = (start_pos.x - end_pos.x) / math.abs(start_pos.x - end_pos.x)
- local start_z_sign = (start_pos.z - end_pos.z) / math.abs(start_pos.z - end_pos.z)
- local end_x_sign = (end_pos.x - start_pos.x) / math.abs(end_pos.x - start_pos.x)
- local end_z_sign = (end_pos.z - start_pos.z) / math.abs(end_pos.z - start_pos.z)
-
- -- Correct the signs if they are nan
- if math.abs(start_pos.x - end_pos.x) == 0 then
- start_x_sign = -1
- end_x_sign = 1
- end
- if math.abs(start_pos.z - end_pos.z) == 0 then
- start_z_sign = -1
- end_z_sign = 1
- end
-
- -- Get starting and ending positions, adding the extra nodes to the area
- local pos1 = {x=start_pos.x + (extra_range * start_x_sign), y = start_pos.y - 1, z=start_pos.z + (extra_range * start_z_sign)}
- local pos2 = {x=end_pos.x + (extra_range * end_x_sign), y = end_pos.y, z=end_pos.z + (extra_range * end_z_sign)}
-
- minetest.log("Recalculated pos1: "..minetest.pos_to_string(pos1))
- minetest.log("Recalculated pos2: "..minetest.pos_to_string(pos2))
-
- local grid = {}
-
- -- Loop through the area and classify nodes
- for z = 1, math.abs(pos1.z - pos2.z) do
- --local current_row = {}
- for x = 1, math.abs(pos1.x - pos2.x) do
- -- Calculate current position
- local current_pos = {x=pos1.x + (x*end_x_sign), y=pos1.y, z=pos1.z + (z*end_z_sign)}
- -- Get node info
- local node = minetest.get_node(current_pos)
- -- Check if this is the starting position
- if current_pos.x == start_pos.x and current_pos.z == start_pos.z then
- -- Is start position
- table.insert(grid, {name=node.name, pos=current_pos, type=pathfinder.node_types.start})
- elseif current_pos.x == end_pos.x and current_pos.z == end_pos.z then
- -- Is ending position or goal position
- table.insert(grid, {name=node.name, pos=current_pos, type=pathfinder.node_types.goal})
- else
- -- Check if node is walkable
- if node.name == "air" then
- -- If air do no more checks
- table.insert(grid, {name=node.name, pos=current_pos, type=pathfinder.node_types.walkable})
- else
- -- Check if it is of a walkable or openable type
- table.insert(grid, {name=node.name, pos=current_pos, type=finder.is_good_node(node, walkables)})
- end
- end
- end
- -- Insert the converted row into the grid
- --table.insert(grid, current_row)
- end
-
- return grid
-end
-
---------------------------------------------------------------------------
--- A* algorithm implementation
---------------------------------------------------------------------------
--- Utility functions
-function finder.distance(node1, node2)
- return math.sqrt(math.pow(node2.pos.x - node1.pos.x, 2) + math.pow(node2.pos.z - node1.pos.z, 2))
-end
-
-function finder.is_valid_neighbor(node1, node2)
- -- Consider only orthogonal nodes
- if (node1.pos.x == node2.pos.x and node1.pos.z ~= node2.pos.z)
- or (node1.pos.z == node2.pos.z and node1.pos.x ~= node2.pos.z) then
- if (finder.distance(node1, node2) < 2) then
- return finder.is_good_node(node2, {})
- end
- end
- return false
-end
-
-function finder.cost_estimate(node1, node2)
- return finder.distance(node1, node2)
-end
-
-function finder.get_lowest_fscore(nodes, f_scores)
- local lowest = 1/0
- local best_node = nil
- for _, node in pairs(nodes) do
- local score = f_scores[node]
- if score < lowest then
- lowest = score
- best_node = node
- end
- end
- return best_node
-end
-
-function finder.get_neighbor(node, nodes)
- local neighbors = {}
- for _, current_node in pairs(nodes) do
- if current_node ~= node then
- if finder.is_valid_neighbor(node, current_node) then
- table.insert(neighbors, current_node)
- end
- end
- end
- return neighbors
-end
-
-function finder.contains_node(node, all_nodes)
- for _,current_node in pairs(all_nodes) do
- if current_node == node then
- return true
- end
- end
- return false
-end
-
-function finder.remove_node(node, all_nodes)
- --minetest.log("On remove_node: "..dump(all_nodes))
- for key, current_node in pairs(all_nodes) do
- if current_node == node then
- --minetest.log("Table before: "..dump(all_nodes))
- table.remove(all_nodes, key)
- --minetest.log("Table after: "..dump(all_nodes))
- return
- end
- end
-end
-
-function finder.create_path(path, grid, node)
- if grid[node] ~= nil then
- table.insert(path, 1, grid[node])
- return create_path(path, grid, grid[node])
- else
- return path
- end
-end
-
-function finder.astar(start_pos, end_pos, nodes)
-
- local closed_set = {}
- local open_set = {
- start_pos
- }
- local came_from = {}
-
- local g_score = {}
- local f_score = {}
- g_score[start_pos] = 0
- f_score[start_pos] = g_score[start_pos] + finder.cost_estimate(start_pos, end_pos)
-
- minetest.log("Open set: "..dump(#open_set))
-
- while #open_set > 0 do
- minetest.log("Nodes size: "..dump(#nodes))
-
- local current = finder.get_lowest_fscore(open_set, f_score)
- minetest.log("Node with best fscore: "..dump(current))
- if current == end_pos then
- minetest.log("Creating path: "..dump(came_from))
- local path = create_path({}, came_from, end_pos)
- table.insert(path, end_pos)
- return path
- end
-
- minetest.log("Removing node from openset..."..dump(#open_set))
- finder.remove_node(current, open_set)
- minetest.log("Removed. Open set size: "..dump(#open_set))
- minetest.log("Adding to closed set..."..dump(#closed_set))
- table.insert(closed_set, current)
- minetest.log("Added. New closed set size: "..dump(#closed_set))
-
- local neighbors = finder.get_neighbor(current, nodes)
- minetest.log("Found "..dump(#neighbors).." neighbors for current node")--dump(neighbors))
- for _, neighbor in pairs(neighbors) do
- minetest.log("Currently looking at neighbor: "..minetest.pos_to_string(neighbor.pos))
- if finder.contains_node(neighbor, closed_set) == false then
- minetest.log("Node is not in closed set")
-
- local tentative_g_score = g_score[current] + finder.distance(current, neighbor)
- minetest.log("Tentative g score is: "..dump(tentative_g_score))
- minetest.log("Logic: "..dump(finder.contains_node(neighbor, open_set) == false or tentative_g_score < g_score[neighbor]))
- if finder.contains_node(neighbor, open_set) == false or tentative_g_score < g_score[neighbor] then
- came_from[neighbor] = current
- minetest.log("Added node to came_from set: "..dump(table.getn(came_from)))
- g_score[neighbor] = tentative_g_score
- f_score[neighbor] = g_score[neighbor] + finder.cost_estimate(neighbor, end_pos)
- if finder.contains_node(neighbor, open_set) == false then
- minetest.log("Adding neighbor node to open_set: "..dump(#open_set))
- table.insert(open_set, neighbor)
- minetest.log("Added. New open set size: "..dump(#open_set))
- end
- end
- end
- end
- end
- -- Path not found
- return nil
-end
diff --git a/npc.lua b/npc.lua
index e80a728..36270a5 100755
--- a/npc.lua
+++ b/npc.lua
@@ -273,7 +273,7 @@ function npc.initialize(entity, pos, is_lua_entity)
minetest.log("Self destination: "..minetest.pos_to_string(nodes[1]))
- local path = pathfinder.find_path(ent.object:getpos(), nodes[1], 20, {})
+ --local path = pathfinder.find_path(ent.object:getpos(), nodes[1], 20, {})
--minetest.log("Path to node: "..dump(path))
--npc.add_action(ent, npc.actions.use_door, {self = ent, pos = nodes[1], action = npc.actions.door_action.OPEN})
--npc.add_action(ent, npc.actions.stand, {self = ent})