control framework renamed to movement framework
This commit is contained in:
parent
a629c0e8dd
commit
268fe91bf8
44
README.md
44
README.md
@ -373,40 +373,52 @@ Submitted data can then be captured in the NPC's own 'on_receive_fields' callbac
|
||||
Note that form text fields, dropdown, list and checkbox selections are automatically
|
||||
stored in the NPC's metadata table. Image/Button clicks, however, are not.
|
||||
|
||||
Control Framework
|
||||
Movement Framework
|
||||
----
|
||||
## Methods
|
||||
### npcf.control_framework.getControl(npc_ref)
|
||||
|
||||
Constructor for the control object. Returns the reference.
|
||||
### mvobj = npcf.movement.getControl(npc_ref)
|
||||
|
||||
Constructor for the movement control object. Returns the reference.
|
||||
Note, the framework will be activated for NPC on first usage.
|
||||
|
||||
### control:stay()
|
||||
### mvobj:stay()
|
||||
Stop walking, stand up
|
||||
|
||||
### control:look_to(pos)
|
||||
### mvobj:look_to(pos)
|
||||
Look (set yaw) to direction of position pos
|
||||
|
||||
### control:sit()
|
||||
### mvobj:sit()
|
||||
Stop walking and sit down
|
||||
|
||||
### control:lay()
|
||||
### mvobj:lay()
|
||||
Stop walking and lay down
|
||||
|
||||
### control:mine()
|
||||
### mvobj:mine()
|
||||
Begin the mining / digging / attacking animation
|
||||
|
||||
### control:mine_stop()
|
||||
### mvobj:mine_stop()
|
||||
Stop the mining / digging / attacking animation
|
||||
|
||||
### control:walk(pos, speed, parameter)
|
||||
### mvobj:teleport(pos)
|
||||
Teleport the NPC to given position
|
||||
|
||||
### mvobj:walk(pos, speed, parameter)
|
||||
Find the way and walk to position pos with given speed.
|
||||
For parameter check the set_walk_parameter documentation
|
||||
|
||||
###control_proto:stop()
|
||||
###mvobj:stop()
|
||||
Stay and forgot about the destination
|
||||
|
||||
### control:set_walk_parameter(parameter)
|
||||
|
||||
###mvobj:get_path(pos)
|
||||
Calculate the path. Is used internally in mvobj:walk
|
||||
|
||||
###mvobj:check_for_stuck()
|
||||
Check if the NPC is stuck. Teleport or stay in this case.
|
||||
This method is called in framework each step so there is no need to call it by self
|
||||
|
||||
### mvobj:set_walk_parameter(parameter)
|
||||
key-value table to change the walking path determination parameter
|
||||
|
||||
- find_path
|
||||
@ -431,7 +443,7 @@ key-value table to change the walking path determination parameter
|
||||
false: forgot about the destination in case of stuck
|
||||
|
||||
## Attributes (should be used read-only)
|
||||
control.is_mining - mining animation is active
|
||||
control.speed - walking speed
|
||||
control.real_speed - The "real" speed calculated on velocity
|
||||
target_pos - Position vector that the NPC try to reach
|
||||
mvobj.is_mining - mining animation is active
|
||||
mvobj.speed - walking speed
|
||||
mvobj.real_speed - The "real" speed calculated on velocity
|
||||
mvobj.target_pos - Position vector that the NPC try to reach
|
||||
|
@ -1,5 +1,5 @@
|
||||
-- NPC framework navigation control object prototype
|
||||
local control_proto = {
|
||||
local mvobj_proto = {
|
||||
is_mining = false,
|
||||
speed = 0,
|
||||
target_pos = nil,
|
||||
@ -18,39 +18,39 @@ local control_proto = {
|
||||
}
|
||||
|
||||
-- navigation control framework
|
||||
local control_framework = {
|
||||
control_proto = control_proto,
|
||||
local movement = {
|
||||
mvobj_proto = mvobj_proto,
|
||||
functions = {},
|
||||
getControl = function(npc)
|
||||
local control
|
||||
if npc._control then
|
||||
control = npc._control
|
||||
local mvobj
|
||||
if npc._mvobj then
|
||||
mvobj = npc._mvobj
|
||||
else
|
||||
control = npcf.deepcopy(control_proto)
|
||||
control._npc = npc
|
||||
npc._control = control
|
||||
mvobj = npcf.deepcopy(mvobj_proto)
|
||||
mvobj._npc = npc
|
||||
npc._mvobj = mvobj
|
||||
end
|
||||
if npc.object and control._step_init_done ~= true then
|
||||
control.pos = npc.object:getpos()
|
||||
control.yaw = npc.object:getyaw()
|
||||
control.velocity = npc.object:getvelocity()
|
||||
control.acceleration = npc.object:getacceleration()
|
||||
control._step_init_done = true
|
||||
if npc.object and mvobj._step_init_done ~= true then
|
||||
mvobj.pos = npc.object:getpos()
|
||||
mvobj.yaw = npc.object:getyaw()
|
||||
mvobj.velocity = npc.object:getvelocity()
|
||||
mvobj.acceleration = npc.object:getacceleration()
|
||||
mvobj._step_init_done = true
|
||||
end
|
||||
return control
|
||||
return mvobj
|
||||
end
|
||||
}
|
||||
local functions = control_framework.functions
|
||||
local functions = movement.functions
|
||||
|
||||
|
||||
-- Stop walking and stand up
|
||||
function control_proto:stay()
|
||||
function mvobj_proto:stay()
|
||||
self.speed = 0
|
||||
self._state = NPCF_ANIM_STAND
|
||||
end
|
||||
|
||||
-- Stay and forgot about the way
|
||||
function control_proto:stop()
|
||||
function mvobj_proto:stop()
|
||||
self:stay()
|
||||
self._path = nil
|
||||
self._target_pos_bak = nil
|
||||
@ -59,43 +59,50 @@ function control_proto:stop()
|
||||
end
|
||||
|
||||
-- look to position
|
||||
function control_proto:look_to(pos)
|
||||
function mvobj_proto:look_to(pos)
|
||||
self.yaw = npcf:get_face_direction(self.pos, pos)
|
||||
end
|
||||
|
||||
-- Stop walking and sitting down
|
||||
function control_proto:sit()
|
||||
function mvobj_proto:sit()
|
||||
self.speed = 0
|
||||
self.is_mining = false
|
||||
self._state = NPCF_ANIM_SIT
|
||||
end
|
||||
|
||||
-- Stop walking and lay
|
||||
function control_proto:lay()
|
||||
function mvobj_proto:lay()
|
||||
self.speed = 0
|
||||
self.is_mining = false
|
||||
self._state = NPCF_ANIM_LAY
|
||||
end
|
||||
|
||||
-- Start mining
|
||||
function control_proto:mine()
|
||||
function mvobj_proto:mine()
|
||||
self.is_mining = true
|
||||
end
|
||||
|
||||
-- Stop mining
|
||||
function control_proto:mine_stop()
|
||||
function mvobj_proto:mine_stop()
|
||||
self.is_mining = false
|
||||
end
|
||||
|
||||
-- teleport to position
|
||||
function mvobj_proto:teleport(pos)
|
||||
self.pos = pos
|
||||
self._npc.object:setpos(pos)
|
||||
self:stay()
|
||||
end
|
||||
|
||||
-- Change default parameters for walking
|
||||
function control_proto:set_walk_parameter(param)
|
||||
function mvobj_proto:set_walk_parameter(param)
|
||||
for k,v in pairs(param) do
|
||||
self.walk_param[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- start walking to pos
|
||||
function control_proto:walk(pos, speed, param)
|
||||
function mvobj_proto:walk(pos, speed, param)
|
||||
if param then
|
||||
self:set_walk_parameter(param)
|
||||
end
|
||||
@ -103,7 +110,7 @@ function control_proto:walk(pos, speed, param)
|
||||
self.target_pos = pos
|
||||
self.speed = speed
|
||||
if self.walk_param.find_path == true then
|
||||
self._path = functions.get_path(self, pos)
|
||||
self._path = self:get_path(pos)
|
||||
else
|
||||
self._path = { pos }
|
||||
self._path_used = false
|
||||
@ -118,17 +125,17 @@ function control_proto:walk(pos, speed, param)
|
||||
end
|
||||
|
||||
-- do a walking step
|
||||
function control_proto:_do_control_step(dtime)
|
||||
function mvobj_proto:_do_movement_step(dtime)
|
||||
-- step timing / initialization check
|
||||
self._step_timer = self._step_timer + dtime
|
||||
if self._step_timer < 0.1 then
|
||||
return
|
||||
end
|
||||
self._step_timer = 0
|
||||
control_framework.getControl(self._npc)
|
||||
movement.getControl(self._npc)
|
||||
self._step_init_done = false
|
||||
|
||||
functions.check_for_stuck(self)
|
||||
self:check_for_stuck()
|
||||
|
||||
-- check path
|
||||
if self.speed > 0 then
|
||||
@ -215,6 +222,82 @@ function control_proto:_do_control_step(dtime)
|
||||
self._npc.object:setvelocity(self.velocity)
|
||||
end
|
||||
|
||||
|
||||
function mvobj_proto:get_path(pos)
|
||||
local startpos = vector.round(self.pos)
|
||||
startpos.y = startpos.y - 1 -- NPC is to high
|
||||
local refpos
|
||||
if vector.distance(self.pos, pos) > self.walk_param.find_path_max_distance then
|
||||
refpos = vector.add(self.pos, vector.multiply(vector.direction(self.pos, pos), self.walk_param.find_path_max_distance))
|
||||
else
|
||||
refpos = pos
|
||||
end
|
||||
|
||||
local destpos
|
||||
if self.walk_param.fuzzy_destination == true then
|
||||
destpos = functions.get_walkable_pos(refpos, self.walk_param.fuzzy_destination_distance)
|
||||
end
|
||||
if not destpos then
|
||||
destpos = self.pos
|
||||
end
|
||||
local path = minetest.find_path(startpos, destpos, 10, 1, 5, "Dijkstra")
|
||||
|
||||
if not path and self.walk_param.find_path_fallback == true then
|
||||
path = { destpos, pos }
|
||||
self._path_used = false
|
||||
--print("fallback path to "..minetest.pos_to_string(pos))
|
||||
elseif path then
|
||||
--print("calculated path to "..minetest.pos_to_string(destpos).."for destination"..minetest.pos_to_string(pos))
|
||||
self._path_used = true
|
||||
table.insert(path, pos)
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function mvobj_proto:check_for_stuck()
|
||||
|
||||
-- high difference stuck
|
||||
if self.walk_param.teleport_on_stuck == true and self.target_pos then
|
||||
local teleport_dest
|
||||
-- Big jump / teleport up- or downsite
|
||||
if math.abs(self.pos.x - self.target_pos.x) <= 1 and
|
||||
math.abs(self.pos.z - self.target_pos.z) <= 1 and
|
||||
vector.distance(self.pos, self.target_pos) > 3 then
|
||||
teleport_dest = table.copy(self.target_pos)
|
||||
teleport_dest.y = teleport_dest.y + 1.5 -- teleport over the destination
|
||||
--print("big-jump teleport to "..minetest.pos_to_string(teleport_dest).." for target "..minetest.pos_to_string(self.target_pos))
|
||||
self:teleport(teleport_dest)
|
||||
end
|
||||
end
|
||||
|
||||
-- stuck check by distance and speed
|
||||
if (self._target_pos_bak and self.target_pos and self.speed > 0 and
|
||||
self._path_used ~= true and self._last_distance and
|
||||
self._target_pos_bak.x == self.target_pos.x and
|
||||
self._target_pos_bak.y == self.target_pos.y and
|
||||
self._target_pos_bak.z == self.target_pos.z and
|
||||
self._last_distance -0.01 <= vector.distance(self.pos, self.target_pos)) or
|
||||
( self._walk_started ~= true and self.speed > 0 and
|
||||
math.sqrt( math.pow(self.velocity.x,2) + math.pow(self.velocity.z,2)) < (self.speed/3)) then
|
||||
--print("Stuck")
|
||||
if self.walk_param.teleport_on_stuck == true then
|
||||
local teleport_dest
|
||||
if vector.distance(self.pos, self.target_pos) > 5 then
|
||||
teleport_dest = vector.add(self.pos, vector.multiply(vector.direction(self.pos, self.target_pos), 5)) -- 5 nodes teleport step
|
||||
else
|
||||
teleport_dest = table.copy(self.target_pos)
|
||||
teleport_dest.y = teleport_dest.y + 1.5 -- teleport over the destination
|
||||
end
|
||||
self:teleport(teleport_dest)
|
||||
else
|
||||
self:stay()
|
||||
end
|
||||
elseif self.target_pos then
|
||||
self._last_distance = vector.distance(self.pos, self.target_pos)
|
||||
end
|
||||
self._walk_started = false
|
||||
end
|
||||
|
||||
---------------------------------------------------------------
|
||||
-- define framework functions internally used
|
||||
---------------------------------------------------------------
|
||||
@ -243,85 +326,7 @@ function functions.get_walkable_pos(pos, dist)
|
||||
return destpos
|
||||
end
|
||||
|
||||
function functions.get_path(control, pos)
|
||||
local startpos = vector.round(control.pos)
|
||||
startpos.y = startpos.y - 1 -- NPC is to high
|
||||
local refpos
|
||||
if vector.distance(control.pos, pos) > control.walk_param.find_path_max_distance then
|
||||
refpos = vector.add(control.pos, vector.multiply(vector.direction(control.pos, pos), control.walk_param.find_path_max_distance))
|
||||
else
|
||||
refpos = pos
|
||||
end
|
||||
|
||||
local destpos
|
||||
if control.walk_param.fuzzy_destination == true then
|
||||
destpos = functions.get_walkable_pos(refpos, control.walk_param.fuzzy_destination_distance)
|
||||
end
|
||||
if not destpos then
|
||||
destpos = control.pos
|
||||
end
|
||||
local path = minetest.find_path(startpos, destpos, 10, 1, 5, "Dijkstra")
|
||||
|
||||
if not path and control.walk_param.find_path_fallback == true then
|
||||
path = { destpos, pos }
|
||||
control._path_used = false
|
||||
--print("fallback path to "..minetest.pos_to_string(pos))
|
||||
elseif path then
|
||||
--print("calculated path to "..minetest.pos_to_string(destpos).."for destination"..minetest.pos_to_string(pos))
|
||||
control._path_used = true
|
||||
table.insert(path, pos)
|
||||
end
|
||||
return path
|
||||
end
|
||||
|
||||
function functions.check_for_stuck(control)
|
||||
|
||||
-- high difference stuck
|
||||
if control.walk_param.teleport_on_stuck == true and control.target_pos then
|
||||
local teleport_dest
|
||||
-- Big jump / teleport up- or downsite
|
||||
if math.abs(control.pos.x - control.target_pos.x) <= 1 and
|
||||
math.abs(control.pos.z - control.target_pos.z) <= 1 and
|
||||
vector.distance(control.pos, control.target_pos) > 3 then
|
||||
teleport_dest = table.copy(control.target_pos)
|
||||
teleport_dest.y = teleport_dest.y + 1.5 -- teleport over the destination
|
||||
control.pos = teleport_dest
|
||||
control._npc.object:setpos(control.pos)
|
||||
control:stay()
|
||||
--print("big-jump teleport to "..minetest.pos_to_string(teleport_dest).." for target "..minetest.pos_to_string(control.target_pos))
|
||||
end
|
||||
end
|
||||
|
||||
-- stuck check by distance and speed
|
||||
if (control._target_pos_bak and control.target_pos and control.speed > 0 and
|
||||
control._path_used ~= true and control._last_distance and
|
||||
control._target_pos_bak.x == control.target_pos.x and
|
||||
control._target_pos_bak.y == control.target_pos.y and
|
||||
control._target_pos_bak.z == control.target_pos.z and
|
||||
control._last_distance -0.01 <= vector.distance(control.pos, control.target_pos)) or
|
||||
( control._walk_started ~= true and control.speed > 0 and
|
||||
math.sqrt( math.pow(control.velocity.x,2) + math.pow(control.velocity.z,2)) < (control.speed/3)) then
|
||||
--print("Stuck")
|
||||
if control.walk_param.teleport_on_stuck == true then
|
||||
local teleport_dest
|
||||
if vector.distance(control.pos, control.target_pos) > 5 then
|
||||
teleport_dest = vector.add(control.pos, vector.multiply(vector.direction(control.pos, control.target_pos), 5)) -- 5 nodes teleport step
|
||||
else
|
||||
teleport_dest = table.copy(control.target_pos)
|
||||
teleport_dest.y = teleport_dest.y + 1.5 -- teleport over the destination
|
||||
end
|
||||
control.pos = teleport_dest
|
||||
control._npc.object:setpos(control.pos)
|
||||
control:stay()
|
||||
else
|
||||
control:stay()
|
||||
end
|
||||
elseif control.target_pos then
|
||||
control._last_distance = vector.distance(control.pos, control.target_pos)
|
||||
end
|
||||
control._walk_started = false
|
||||
end
|
||||
---------------------------------------------------------------
|
||||
-- Return the framework to calling function
|
||||
---------------------------------------------------------------
|
||||
return control_framework
|
||||
return movement
|
@ -73,8 +73,8 @@ npcf = {
|
||||
animation_state = 0,
|
||||
animation_speed = 30,
|
||||
},
|
||||
-- control functions
|
||||
control_framework = dofile(NPCF_MODPATH.."/control.lua"),
|
||||
-- movement control functions
|
||||
movement = dofile(NPCF_MODPATH.."/movement.lua"),
|
||||
deepcopy = deepcopy
|
||||
}
|
||||
|
||||
@ -242,8 +242,8 @@ function npcf:register_npc(name, def)
|
||||
if type(def.on_step) == "function" then
|
||||
self.timer = self.timer + dtime
|
||||
def.on_step(self, dtime)
|
||||
if self._control then
|
||||
self._control:_do_control_step(dtime)
|
||||
if self._mvobj then
|
||||
self._mvobj:_do_movement_step(dtime)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user