sapier ccbf8fae5d mgen path: add support for runtime single path via "set_path"
mgen path: add target reached callback "set_end_of_path_handler"
2013-09-18 19:46:25 +02:00

347 lines
10 KiB
Lua

-------------------------------------------------------------------------------
-- Mob Framework Mod by Sapier
--
-- You may copy, use, modify or do nearly anything except removing this
-- copyright notice.
-- And of course you are NOT allow to pretend you have written it.
--
--! @file path_based_movement_gen.lua
--! @brief component containing a path based movement generator
--! @copyright Sapier
--! @author Sapier
--! @date 2012-08-09
--
--! @defgroup mgen_path_based MGEN: Path based movement generator
--! @ingroup framework_int
--! @{
-- Contact sapier a t gmx net
-------------------------------------------------------------------------------
--! @class p_mov_gen
--! @brief a movement generator evaluating a path to a target and following it
--!@}
p_mov_gen = {}
--! @brief movement generator identifier
--! @memberof p_mov_gen
p_mov_gen.name = "mgen_path"
-------------------------------------------------------------------------------
-- name: callback(entity,now)
--
--! @brief path based movement generator callback
--! @memberof p_mov_gen
--
-- param1: mob to do movement
-- param2: current time
-- retval: -
-------------------------------------------------------------------------------
function p_mov_gen.callback(entity,now,dstep)
mobf_assert_backtrace(entity ~= nil)
mobf_assert_backtrace(entity.dynamic_data ~= nil)
mobf_assert_backtrace(entity.dynamic_data.p_movement ~= nil)
if entity.dynamic_data.p_movement.eta ~= nil then
if now < entity.dynamic_data.p_movement.eta then
return
end
end
local current_pos = entity.object:getpos()
local handled = false
if entity.dynamic_data.p_movement.path == nil then
dbg_mobf.path_mov_lvl1(
"MOBF: path movement but mo path set!!")
return
end
local max_distance = entity.data.movement.max_distance
if entity.dynamic_data.movement.max_distance ~= nil then
max_distance = entity.dynamic_data.movement.max_distance
end
mobf_assert_backtrace(entity.dynamic_data.p_movement.next_path_index ~= nil)
mobf_assert_backtrace(max_distance ~= nil)
--check if target is reached
if p_mov_gen.distance_to_next_point(entity,current_pos)
< max_distance then
dbg_mobf.path_mov_lvl1("MOBF: pathmov next to next point switching target")
local update_target = true
--return to begining of path
if entity.dynamic_data.p_movement.next_path_index
== #entity.dynamic_data.p_movement.path then
if entity.dynamic_data.p_movement.cycle_path or
(entity.dynamic_data.p_movement.cycle_path == nil and
entity.data.patrol ~= nil and
entity.data.patrol.cycle_path) then
--0 is correct as it's incremented by one later
entity.dynamic_data.p_movement.next_path_index = 0
else
if entity.dynamic_data.p_movement.HANDLER_end_of_path ~= nil
and type(entity.dynamic_data.p_movement.HANDLER_end_of_path) == "function" then
entity.dynamic_data.p_movement.HANDLER_end_of_path(entity)
end
dbg_mobf.path_mov_lvl1("MOBF: cycle not set not updating point")
update_target = false
handled = true
end
end
if update_target then
mobf_assert_backtrace(entity.dynamic_data.p_movement.path ~= nil)
entity.dynamic_data.p_movement.next_path_index =
entity.dynamic_data.p_movement.next_path_index + 1
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path
[entity.dynamic_data.p_movement.next_path_index]
dbg_mobf.path_mov_lvl1("MOBF: (1) setting new target to index: " ..
entity.dynamic_data.p_movement.next_path_index .. " pos: " ..
printpos(entity.dynamic_data.movement.target))
handled = true
end
end
if not handled and
entity.dynamic_data.movement.target == nil then
mobf_assert_backtrace(entity.dynamic_data.p_movement.path ~= nil)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path
[entity.dynamic_data.p_movement.next_path_index]
dbg_mobf.path_mov_lvl1("MOBF: (2) setting new target to index: " ..
entity.dynamic_data.p_movement.next_path_index .. " pos: " ..
printpos(entity.dynamic_data.movement.target))
end
mgen_follow.callback(entity,now)
end
-------------------------------------------------------------------------------
-- name: distance_to_next_point(entity)
--
--! @brief get distance to next target point (2d only)
--! @memberof p_mov_gen
--! @private
--
--! @param entity mob to check
--! @param current_pos position mob is atm
--
--! @retval distance
-------------------------------------------------------------------------------
function p_mov_gen.distance_to_next_point(entity,current_pos)
local index = entity.dynamic_data.p_movement.next_path_index
mobf_assert_backtrace(entity.dynamic_data.p_movement.path ~= nil)
mobf_assert_backtrace(index <= #entity.dynamic_data.p_movement.path)
return mobf_calc_distance_2d(current_pos,
entity.dynamic_data.p_movement.path[index])
end
-------------------------------------------------------------------------------
-- name: init_dynamic_data(entity,now)
--
--! @brief initialize dynamic data required by movement generator
--! @memberof p_mov_gen
--
--! @param entity to initialize
--! @param now current time
--! @param restored_data data restored on activate
-------------------------------------------------------------------------------
function p_mov_gen.init_dynamic_data(entity,now,restored_data)
local pos = entity.object:getpos()
local data = {
path = nil,
eta = nil,
last_move_stop = now,
next_path_index = 1,
force_target = nil,
pathowner = nil,
pathname = nil,
}
if restored_data ~= nil then
dbg_mobf.path_mov_lvl3(
"MOBF: path movement reading stored data: " .. dump(restored_data))
if restored_data.pathowner ~= nil and
restored_data.pathname ~= nil then
data.pathowner = restored_data.pathowner
data.pathname = restored_data.pathname
data.path = mobf_path.getpoints(data.pathowner,data.pathname)
dbg_mobf.path_mov_lvl3(
"MOBF: path movement restored points: " .. dump(data.path))
end
if restored_data.pathindex ~= nil and
type(restored_data.pathindex) == "number" and
restored_data.pathindex > 0 and
data.path ~= nil and
restored_data.pathindex < #data.path then
data.next_path_index = restored_data.pathindex
end
end
entity.dynamic_data.p_movement = data
mgen_follow.init_dynamic_data(entity,now)
end
-------------------------------------------------------------------------------
-- name: set_path(entity,path)
--
--! @brief set target for movgen
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param path to set
-------------------------------------------------------------------------------
function p_mov_gen.set_path(entity,path)
mobf_assert_backtrace(entity.dynamic_data.p_movement ~= nil)
if path ~= nil then
entity.dynamic_data.p_movement.next_path_index = 1
entity.dynamic_data.movement.max_distance = 0.5
entity.dynamic_data.p_movement.path = path
--a valid path has at least 2 positions
mobf_assert_backtrace(#entity.dynamic_data.p_movement.path > 1)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path[2]
return true
else
entity.dynamic_data.p_movement.next_path_index = nil
entity.dynamic_data.movement.max_distance = nil
entity.dynamic_data.p_movement.path = nil
entity.dynamic_data.movement.target = nil
return false
end
end
-------------------------------------------------------------------------------
-- name: set_cycle_path(entity,value)
--
--! @brief set state of path cycle mechanism
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param value to set true/false/nil(mob global default)
-------------------------------------------------------------------------------
function p_mov_gen.set_cycle_path(entity,value)
mobf_assert_backtrace(entity.dynamic_data.p_movement ~= nil)
entity.dynamic_data.p_movement.cycle_path = value
end
-------------------------------------------------------------------------------
-- name: set_end_of_path_handler(entity,handler)
--
--! @brief set handler to call for non cyclic paths if final target is reached
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param handler to call at final target
-------------------------------------------------------------------------------
function p_mov_gen.set_end_of_path_handler(entity,handler)
entity.dynamic_data.p_movement.HANDLER_end_of_path = handler
end
-------------------------------------------------------------------------------
-- name: set_target(entity,target)
--
--! @brief set target for movgen
--! @memberof p_mov_gen
--! @public
--
--! @param entity mob to apply to
--! @param target to set
-------------------------------------------------------------------------------
function p_mov_gen.set_target(entity,target)
mobf_assert_backtrace(target ~= nil)
local current_pos = entity.getbasepos(entity)
local targetpos = nil
if not mobf_is_pos(target) then
if target:is_player() then
targetpos = target:getpos()
targetpos.y = targetpos.y +0.5
else
if type(target.getbasepos) == "function" then
targetpos = target.getbasepos(target)
else
targetpos = target:getpos()
end
end
else
targetpos = target
end
if targetpos == nil then
return false
end
if entity.dynamic_data.p_movement.lasttargetpos ~= nil then
if mobf_pos_is_same(entity.dynamic_data.p_movement.lasttargetpos,
targetpos) then
return true
end
end
entity.dynamic_data.p_movement.lasttargetpos = targetpos
entity.dynamic_data.p_movement.path = nil
entity.dynamic_data.p_movement.next_path_index = 1
--on target mode max distance is always 0.5
entity.dynamic_data.movement.max_distance = 0.5
--try to find path on our own
if not mobf_get_world_setting("mobf_disable_pathfinding") then
entity.dynamic_data.p_movement.path =
minetest.find_path(current_pos,targetpos,5,1,1,nil)
else
entity.dynamic_data.p_movement.path = nil
end
if entity.dynamic_data.p_movement.path ~= nil then
--a valid path has at least 2 positions
mobf_assert_backtrace(#entity.dynamic_data.p_movement.path > 1)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path[2]
return true
end
if entity.dynamic_data.p_movement.path == nil then
print(
"MOBF: no pathfinding support/ no path found directly setting targetpos as path")
entity.dynamic_data.p_movement.path = {}
table.insert(entity.dynamic_data.p_movement.path,targetpos)
entity.dynamic_data.movement.target =
entity.dynamic_data.p_movement.path[1]
return true
end
return false
end
--register this movement generator
registerMovementGen(p_mov_gen.name,p_mov_gen)