Path class completed and debugged.
This commit is contained in:
parent
872b9f258f
commit
7815223fc7
159
init.lua
159
init.lua
@ -54,27 +54,33 @@ obj:factor_is_near_mate = ...
|
|||||||
|
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
|
--
|
||||||
|
-- misc functions
|
||||||
|
--
|
||||||
|
|
||||||
function vector.sort(v1, v2)
|
function vector.sort(v1, v2)
|
||||||
return {x = math.min(v1.x, v2.x), y = math.min(v1.y, v2.y), z = math.min(v1.z, v2.z)},
|
return {x = math.min(v1.x, v2.x), y = math.min(v1.y, v2.y), z = math.min(v1.z, v2.z)},
|
||||||
{x = math.max(v1.x, v2.x), y = math.max(v1.y, v2.y), z = math.max(v1.z, v2.z)}
|
{x = math.max(v1.x, v2.x), y = math.max(v1.y, v2.y), z = math.max(v1.z, v2.z)}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dir_to_yaw(vec)
|
--
|
||||||
if vec.z < 0 then
|
-- includes
|
||||||
return math.pi - math.atan(vec.x / vec.z)
|
--
|
||||||
elseif vec.z > 0 then
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
return -math.atan(vec.x / vec.z)
|
|
||||||
elseif vec.x < 0 then
|
dofile(modpath .. "/path.lua")
|
||||||
return math.pi
|
|
||||||
else
|
|
||||||
return 0
|
--
|
||||||
end
|
-- globals
|
||||||
end
|
--
|
||||||
|
|
||||||
|
|
||||||
local drivers = {}
|
local drivers = {}
|
||||||
local factors = {}
|
local factors = {}
|
||||||
|
--
|
||||||
|
-- Animation functions
|
||||||
|
--
|
||||||
|
|
||||||
local function animation_select(self, phase, segment)
|
local function animation_select(self, phase, segment)
|
||||||
local state = self.entity_ai_state
|
local state = self.entity_ai_state
|
||||||
@ -160,81 +166,18 @@ drivers.roam = {
|
|||||||
return
|
return
|
||||||
elseif state.roam_move then
|
elseif state.roam_move then
|
||||||
-- do path movement
|
-- do path movement
|
||||||
local pos = self.object:getpos()
|
if not self.path then
|
||||||
if vector.distance(pos, state.roam_target) < 1.0 then
|
|
||||||
-- arrived (close enough!
|
|
||||||
state.roam_ttl = 0
|
state.roam_ttl = 0
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if state.roam_path then
|
if self.path:distance() < 1.0 then
|
||||||
local curspd = self.object:getvelocity()
|
state.roam_ttl = 0
|
||||||
-- if jumping, let jump finish before making more adjustments
|
return
|
||||||
if curspd.y <= 0.2 and curspd.y >= 0 then
|
end
|
||||||
local i, v = next(state.roam_path, nil)
|
if not self.path:step(dtime) then
|
||||||
if not i then
|
|
||||||
-- pathing failed
|
-- pathing failed
|
||||||
state.roam_ttl = 0
|
state.roam_ttl = 0
|
||||||
return
|
|
||||||
end
|
end
|
||||||
if vector.distance(pos, v) < 0.3 then
|
|
||||||
-- remove one
|
|
||||||
--FIXME shouldn't return here
|
|
||||||
local j = i
|
|
||||||
local i, v = next(state.roam_path, i)
|
|
||||||
if not v then
|
|
||||||
state.roam_path[j] = nil
|
|
||||||
state.roam_ttl = 0
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- prune path more?
|
|
||||||
local ii, vv = next(state.roam_path, i)
|
|
||||||
local iii, vvv = next(state.roam_path, ii)
|
|
||||||
if vv and vvv and vvv.y == v.y and vector.distance(vv,v) < 2 then
|
|
||||||
-- prune one
|
|
||||||
state.roam_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, 2.0)-- vel
|
|
||||||
-- don't jump from too far away
|
|
||||||
if vdif > 0.1 and len < 1.5 then
|
|
||||||
print("jump")
|
|
||||||
-- make sure we finish our jump
|
|
||||||
state.roam_ttl = math.min(3.0, state.roam_ttl)
|
|
||||||
-- jump
|
|
||||||
spd = {x = spd.x/10, y = 5, z = spd.z/10}
|
|
||||||
self.object:setvelocity(spd)
|
|
||||||
elseif vdif < 0 and len <= 1.1 then
|
|
||||||
-- drop one path node just to be sure
|
|
||||||
state.roam_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
|
|
||||||
--print(minetest.pos_to_string(spd))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
else
|
||||||
print("unknown roam state!")
|
print("unknown roam state!")
|
||||||
end
|
end
|
||||||
@ -243,7 +186,6 @@ drivers.roam = {
|
|||||||
state.roam_ttl = math.random(3, 9)
|
state.roam_ttl = math.random(3, 9)
|
||||||
-- flip state
|
-- flip state
|
||||||
if state.roam_idle then
|
if state.roam_idle then
|
||||||
print("going roaming")
|
|
||||||
-- get a target
|
-- get a target
|
||||||
local pos = self.object:getpos()
|
local pos = self.object:getpos()
|
||||||
local minp, maxp = vector.sort({
|
local minp, maxp = vector.sort({
|
||||||
@ -281,6 +223,7 @@ drivers.roam = {
|
|||||||
print("no path found!")
|
print("no path found!")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.add_particle({
|
minetest.add_particle({
|
||||||
pos = {x = pick.x, y = pick.y - 0.1, z = pick.z},
|
pos = {x = pick.x, y = pick.y - 0.1, z = pick.z},
|
||||||
velocity = vector.new(),
|
velocity = vector.new(),
|
||||||
@ -292,46 +235,21 @@ drivers.roam = {
|
|||||||
texture = "wool_red.png",
|
texture = "wool_red.png",
|
||||||
playername = nil
|
playername = nil
|
||||||
})
|
})
|
||||||
state.roam_target = pick
|
|
||||||
-- pathing will fail if we're on a ledge. We can fix this by
|
|
||||||
-- pathing from the node below instead
|
|
||||||
local onpos = vector.round({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 - 0.5
|
|
||||||
end
|
|
||||||
|
|
||||||
state.roam_path = minetest.find_path(pos, pick, 30, 1.0, .0, "Dijkstra")
|
self.path = Path(self, pick)
|
||||||
if not state.roam_path then
|
if not self.path:find() then
|
||||||
print("Unable to calculate path")
|
print("Unable to calculate path")
|
||||||
else
|
return
|
||||||
for k, v in pairs(state.roam_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
|
end
|
||||||
|
|
||||||
-- done, roaming mode good!
|
-- done, roaming mode good!
|
||||||
animation_select(self, "move")
|
animation_select(self, "move")
|
||||||
state.roam_idle = nil
|
state.roam_idle = nil
|
||||||
state.roam_move = true
|
state.roam_move = true
|
||||||
|
|
||||||
else
|
else
|
||||||
print("going idle")
|
|
||||||
animation_select(self, "idle")
|
animation_select(self, "idle")
|
||||||
state.roam_idle = true
|
state.roam_idle = true
|
||||||
state.roam_move = nil
|
state.roam_move = nil
|
||||||
state.roam_target = nil
|
|
||||||
state.roam_path = nil
|
|
||||||
-- stop
|
-- stop
|
||||||
self.object:setvelocity(vector.new())
|
self.object:setvelocity(vector.new())
|
||||||
end
|
end
|
||||||
@ -346,10 +264,12 @@ drivers.roam = {
|
|||||||
|
|
||||||
drivers.startle = {
|
drivers.startle = {
|
||||||
start = function(self)
|
start = function(self)
|
||||||
-- start with moving animation
|
-- startle animation
|
||||||
animation_select(self, "idle")
|
animation_select(self, "idle")
|
||||||
|
self.object:setvelocity(vector.new())
|
||||||
-- clear factors
|
-- clear factors
|
||||||
local state = self.entity_ai_state
|
local state = self.entity_ai_state
|
||||||
|
state.attacker = state.factors.got_hit[1]
|
||||||
state.factors.got_hit = nil
|
state.factors.got_hit = nil
|
||||||
state.factors.anim_end = nil
|
state.factors.anim_end = nil
|
||||||
end,
|
end,
|
||||||
@ -425,6 +345,15 @@ local function entity_ai_on_activate(self, staticdata)
|
|||||||
self.object:remove()
|
self.object:remove()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
-- path class
|
||||||
|
local state = self.entity_ai_state
|
||||||
|
if state.path_save then
|
||||||
|
self.path = Path(self, state.path_save.target)
|
||||||
|
self.path:set_config(state.path_save.config)
|
||||||
|
self.path:find()
|
||||||
|
state.path_save = {}
|
||||||
|
end
|
||||||
|
|
||||||
driver = self.entity_ai_state.driver
|
driver = self.entity_ai_state.driver
|
||||||
print("loaded: " .. self.name .. ", driver=" .. driver)
|
print("loaded: " .. self.name .. ", driver=" .. driver)
|
||||||
else
|
else
|
||||||
@ -449,7 +378,7 @@ end
|
|||||||
|
|
||||||
local function entity_ai_on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
local function entity_ai_on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
||||||
local state = self.entity_ai_state
|
local state = self.entity_ai_state
|
||||||
state.factors["got_hit"] = {puncher, time_from_last_punch, tool_capabilities, dir}
|
state.factors["got_hit"] = {puncher:get_player_name(), time_from_last_punch, tool_capabilities, dir}
|
||||||
if self.object:get_hp() == 0 then
|
if self.object:get_hp() == 0 then
|
||||||
print("death")
|
print("death")
|
||||||
self.object:set_hp(1)
|
self.object:set_hp(1)
|
||||||
@ -464,7 +393,11 @@ end
|
|||||||
|
|
||||||
local function entity_ai_get_staticdata(self)
|
local function entity_ai_get_staticdata(self)
|
||||||
print("saved: " .. self.name)
|
print("saved: " .. self.name)
|
||||||
return minetest.serialize(self.entity_ai_state)
|
local state = self.entity_ai_state
|
||||||
|
if self.path then
|
||||||
|
state.path_save = self.path:save()
|
||||||
|
end
|
||||||
|
return minetest.serialize(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
174
path.lua
Normal file
174
path.lua
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
|
||||||
|
--
|
||||||
|
-- Path class - manage and execute an entity path
|
||||||
|
--
|
||||||
|
|
||||||
|
-- misc helper function
|
||||||
|
local function dir_to_yaw(vec)
|
||||||
|
if vec.z < 0 then
|
||||||
|
return math.pi - math.atan(vec.x / vec.z)
|
||||||
|
elseif vec.z > 0 then
|
||||||
|
return -math.atan(vec.x / vec.z)
|
||||||
|
elseif vec.x < 0 then
|
||||||
|
return math.pi
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Class definition
|
||||||
|
Path = {}
|
||||||
|
Path.__index = Path
|
||||||
|
|
||||||
|
setmetatable(Path, {
|
||||||
|
__call = function(c, ...)
|
||||||
|
return c.new(...)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
-- constructor
|
||||||
|
function Path.new(obj, to)
|
||||||
|
local self = setmetatable({}, Path)
|
||||||
|
self.object = obj.object
|
||||||
|
self.origin = self.object:getpos()
|
||||||
|
self.target = to
|
||||||
|
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()
|
||||||
|
-- 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.2 and curspd.y >= 0 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, 2.0)-- vel
|
||||||
|
-- don't jump from too far away
|
||||||
|
if vdif > 0.1 and len < 1.5 then
|
||||||
|
-- jump
|
||||||
|
spd = {x = spd.x/10, y = 5, z = spd.z/10}
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user