Merge branch 'master' into builder_schemlib

This commit is contained in:
Alexander Weber 2017-01-22 23:37:47 +01:00
commit d6065584b6
5 changed files with 190 additions and 172 deletions

View File

@ -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

View File

@ -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
@ -202,7 +209,6 @@ function control_proto:_do_control_step(dtime)
self._npc.object:setacceleration(self.acceleration)
elseif minetest.registered_nodes[node[-1].name].walkable ~= false and
minetest.registered_nodes[node[0].name].walkable ~= false then
print("up!")
-- jump if in catched in walkable node
self.velocity.y = 3
else
@ -216,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
---------------------------------------------------------------
@ -244,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), 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", 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

View File

@ -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

View File

@ -225,42 +225,44 @@ npcf:register_npc("npcf_builder:npc" ,{
end
end,
on_step = function(self, dtime)
local control = npcf.control_framework.getControl(self)
local mv_obj = npcf.movement.getControl(self)
local pos = mv_obj.pos
if self.timer > 1 then
self.timer = 0
if not self.owner then
return
end
control:mine_stop()
mv_obj:mine_stop()
if self.metadata.building == true then
local nodedata
local schemlib_node
local distance
if not SCHEMLIB_PATH then
nodedata = self.var.nodedata[self.metadata.index]
distance = vector.distance(control.pos, nodedata.pos)
control:walk(nodedata.pos, get_speed(distance), {teleport_on_stuck = true})
distance = vector.distance(pos, nodedata.pos)
mv_obj:walk(nodedata.pos, get_speed(distance), {teleport_on_stuck = true})
else
if not self.my_ai_data then
self.my_ai_data = {}
end
schemlib_node = schemlib.npc_ai.plan_target_get({
plan = self.schemlib_plan,
npcpos = control.pos,
npcpos = pos,
savedata = self.my_ai_data})
if not schemlib_node then --stuck in plan
control:stop()
mv_obj:stop()
if self.schemlib_plan.data.nodecount == 0 then
reset_build(self)
end
return
end
distance = vector.distance(control.pos, schemlib_node.world_pos)
control:walk(schemlib_node.world_pos, get_speed(distance), {teleport_on_stuck = true})
distance = vector.distance(pos, schemlib_node.world_pos)
mv_obj:walk(schemlib_node.world_pos, get_speed(distance), {teleport_on_stuck = true})
end
if distance < 4 then
control:mine()
control.speed = 1
mv_obj:mine()
mv_obj.speed = 1
mv_obj:set_walk_parameter({teleport_on_stuck = false})
if SCHEMLIB_PATH then
schemlib.npc_ai.place_node(schemlib_node, self.schemlib_plan)
self.schemlib_plan:del_node(schemlib_node.plan_pos)
@ -270,8 +272,8 @@ npcf:register_npc("npcf_builder:npc" ,{
self.var.selected = ""
else
self.metadata.building = false
control:mine_stop()
control:stop()
mv_obj:mine_stop()
mv_obj:stop()
end
end
if self.schemlib_plan.data.nodecount == 0 then
@ -281,7 +283,7 @@ npcf:register_npc("npcf_builder:npc" ,{
if minetest.registered_nodes[nodedata.node.name].sounds then
local soundspec = minetest.registered_nodes[nodedata.node.name].sounds.place
if soundspec then
soundspec.pos = control.pos
soundspec.pos = pos
minetest.sound_play(soundspec.name, soundspec)
end
end
@ -292,8 +294,8 @@ npcf:register_npc("npcf_builder:npc" ,{
self.var.selected = ""
else
self.metadata.building = false
control:stop()
control:mine_stop()
mv_obj:stop()
mv_obj:mine_stop()
local i = 0
for k,v in pairs(self.var.nodelist) do
i = i + 1
@ -310,12 +312,12 @@ npcf:register_npc("npcf_builder:npc" ,{
end
end
end
elseif vector.equals(control.pos, self.origin.pos) == false then
local distance = vector.distance(control.pos, self.origin.pos)
elseif vector.equals(pos, self.origin.pos) == false then
local distance = vector.distance(pos, self.origin.pos)
if distance > 1 then
control:walk(self.origin.pos, get_speed(distance), {teleport_on_stuck = true})
mv_obj:walk(self.origin.pos, get_speed(distance), {teleport_on_stuck = true})
else
control.yaw = self.origin.yaw
mv_obj.yaw = self.origin.yaw
end
end
end

View File

@ -101,12 +101,13 @@ npcf:register_npc("npcf_guard:npc", {
end
end,
on_step = function(self, dtime)
local control = npcf.control_framework.getControl(self)
if self.timer > 1 then
local move_obj = npcf.movement.getControl(self)
local pos = move_obj.pos
local target = {object=nil, distance=0}
local min_dist = 1000
control:mine_stop()
for _,object in ipairs(minetest.get_objects_inside_radius(control.pos, TARGET_RADIUS)) do
move_obj:mine_stop()
for _,object in ipairs(minetest.get_objects_inside_radius(pos, TARGET_RADIUS)) do
local to_target = false
if object:is_player() then
if GUARD_ATTACK_PLAYERS == true and self.metadata.attack_players == "true" then
@ -129,10 +130,10 @@ npcf:register_npc("npcf_guard:npc", {
end
if to_target == true then
local op = object:getpos()
local dv = vector.subtract(control.pos, op)
local dv = vector.subtract(pos, op)
local dy = math.abs(dv.y - 1)
if dy < math.abs(dv.x) or dy < math.abs(dv.z) then
local dist = math.floor(vector.distance(control.pos, op))
local dist = math.floor(vector.distance(pos, op))
if dist < min_dist then
target.object = object
target.distance = dist
@ -143,9 +144,9 @@ npcf:register_npc("npcf_guard:npc", {
end
if target.object then
if target.distance < 3 then
control:mine()
control:stay()
control:look_to(target.object:getpos())
move_obj:mine()
move_obj:stay()
move_obj:look_to(target.object:getpos())
local tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=1}}
local item = self.metadata.wielditem
if item ~= "" and minetest.registered_items[item] then
@ -157,19 +158,19 @@ npcf:register_npc("npcf_guard:npc", {
end
if target.distance > 2 then
local speed = get_speed(target.distance) * 1.1
control:walk(target.object:getpos(), speed)
move_obj:walk(target.object:getpos(), speed)
end
elseif self.metadata.follow_owner == "true" then
local player = minetest.get_player_by_name(self.owner)
if player then
local p = player:getpos()
local distance = vector.distance(control.pos, {x=p.x, y=control.pos.y, z=p.z})
local distance = vector.distance(pos, {x=p.x, y=pos.y, z=p.z})
if distance > 3 then
control:walk(p, get_speed(distance))
move_obj:walk(p, get_speed(distance))
else
control:stay()
move_obj:stay()
end
control:mine_stop()
move_obj:mine_stop()
end
elseif self.metadata.patrol == "true" then
self.var.rest_timer = self.var.rest_timer + self.timer
@ -180,25 +181,24 @@ npcf:register_npc("npcf_guard:npc", {
end
local patrol_pos = self.metadata.patrol_points[index]
if patrol_pos then
local distance = vector.distance(control.pos, patrol_pos)
local distance = vector.distance(pos, patrol_pos)
if distance > 1 then
control:walk(patrol_pos, PATROL_SPEED)
move_obj:walk(patrol_pos, PATROL_SPEED)
else
self.object:setpos(patrol_pos)
control:stay()
move_obj:teleport(patrol_pos)
self.metadata.patrol_index = index
self.var.rest_timer = 0
end
end
end
elseif vector.equals(control.pos, self.origin.pos) == false then
local distance = vector.distance(control.pos, self.origin.pos)
elseif vector.equals(pos, self.origin.pos) == false then
local distance = vector.distance(pos, self.origin.pos)
if distance > 1 then
control:walk(self.origin.pos, get_speed(distance))
move_obj:walk(self.origin.pos, get_speed(distance))
else
self.object:setpos(self.origin.pos)
control.look_to(self.origin.pos)
control:stay()
move_obj:teleport(self.origin.pos)
move_obj:stay()
move_obj.yaw = self.origin.yaw
end
end
self.timer = 0