entity_ai/path.lua

190 lines
4.0 KiB
Lua

--
-- Path class - manage and execute an entity path
--
-- Class definition
Path = {}
Path.__index = Path
setmetatable(Path, {
__call = function(c, ...)
return c.new(...)
end,
})
-- constructor
function Path.new(obj)
local self = setmetatable({}, Path)
self.object = obj.object
self.driver = obj.object:get_luaentity().driver
self.origin = self.object:getpos()
self.config = {
distance = 30,
jump = 1.0,
fall = 3.0,
algorithm = "Dijkstra",
}
self.path = {}
return self
end
-- to help serialization
function Path:save()
return {
target = self.target,
config = self.config
}
end
function Path:find(finder)
-- select a finder
if not finder then
local finders = self.object:get_luaentity().script[self.driver.name].finders
if not finders then
print("No finder for driver: " .. self.driver.name)
return false
end
for _, v in ipairs(finders) do
-- use the finder
self.target = entity_ai.registered_finders[v](self.object:get_luaentity())
if self.target then
break
end
end
else
self.target = entity_ai.registered_finders[finder](self.object:get_luaentity())
end
if not self.target then
return false
end
-- pathing will fail if we're on a ledge. We can fix this by
-- pathing from the node below instead
local pos = vector.round(self.origin)
local onpos = {x = pos.x, y = pos.y - 1, z = pos.z}
local on = minetest.get_node(onpos)
if not minetest.registered_nodes[on.name].walkable then
pos.y = onpos.y
end
local config = self.config
self.path = minetest.find_path(pos, vector.round(self.target), config.distance, config.jump,
config.fall, config.algorithm)
--[[
if self.path ~= nil then
for k, v in pairs(self.path) do
minetest.add_particle({
pos = v,
velocity = vector.new(),
acceleration = vector.new(),
expirationtime = 3,
size = 3,
collisiondetection = false,
vertical = false,
texture = "wool_white.png",
playername = nil
})
end
end
--]]
return self.path ~= nil
end
function Path:step(dtime)
local curspd = self.object:getvelocity()
local pos = self.object:getpos()
-- if jumping, let jump finish before making more adjustments
if curspd.y >= 0 and curspd.y <= 2 then
local i, v = next(self.path, nil)
if not i then
return false
end
if vector.distance(pos, v) < 0.3 then
-- remove one
--FIXME shouldn't return here
local j = i
local i, v = next(self.path, i)
if not v then
return false
end
end
-- prune path more?
local ii, vv = next(self.path, i)
local iii, vvv = next(self.path, ii)
if vv and vvv and vvv.y == v.y and vector.distance(vv,v) < 2 then
-- prune one
self.path[ii] = nil
end
-- done pruning
--[[
minetest.add_particle({
pos = {x = v.x, y = v.y + 0.2, z = v.z},
velocity = vector.new(),
acceleration = vector.new(),
expirationtime = 1,
size = 2,
collisiondetection = false,
vertical = false,
texture = "wool_yellow.png",
playername = nil
})
--]]
local vo = {x = v.x, y = v.y - 0.5, z = v.z}
local vec = vector.subtract(vo, pos)
local len = vector.length(vec)
local vdif = vec.y
vec.y = 0
local dir = vector.normalize(vec)
local spd = vector.multiply(dir, self.driver:get_property("speed"))
-- don't jump from too far away
if vdif > 0.1 and len < 1.5 then
-- jump
spd = {x = spd.x/4, y = 5, z = spd.z/4}
self.object:setvelocity(spd)
elseif vdif < 0 and len <= 1.1 then
-- drop one path node just to be sure
self.path[i] = nil
-- falling down, just let if fall
else
spd.y = self.object:getvelocity().y
-- don't change yaw when jumping
self.object:setyaw(dir_to_yaw(spd))
self.object:setvelocity(spd)
end
end
return true
end
function Path:distance()
if not self.path then
return 0
end
if not self.target then
return 0
end
return vector.distance(self.object:getpos(), self.target)
end
function Path:length()
if not self.path then
return 0
end
return #self.path
end
function Path:get_config()
return self.config
end
function Path:set_config(conf)
self.config = conf
end