From 88adddaf349559d1bd8daf20c203c3bc27096796 Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 08:39:02 +0100 Subject: [PATCH 1/8] use pos as local variable like old implementation in npcf_builder and npcf_guard --- npcf_builder/init.lua | 9 +++++---- npcf_guard/init.lua | 15 ++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/npcf_builder/init.lua b/npcf_builder/init.lua index b6c23d1..c13ac24 100644 --- a/npcf_builder/init.lua +++ b/npcf_builder/init.lua @@ -201,6 +201,7 @@ npcf:register_npc("npcf_builder:npc" ,{ end, on_step = function(self, dtime) local control = npcf.control_framework.getControl(self) + local pos = control.pos if self.timer > 1 then self.timer = 0 if not self.owner then @@ -209,7 +210,7 @@ npcf:register_npc("npcf_builder:npc" ,{ control:mine_stop() if self.metadata.building == true then local nodedata = self.var.nodedata[self.metadata.index] - local distance = vector.distance(control.pos, nodedata.pos) + local distance = vector.distance(pos, nodedata.pos) control:walk(nodedata.pos, get_speed(distance), {teleport_on_stuck = true}) if distance < 4 then control:mine() @@ -217,7 +218,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 @@ -245,8 +246,8 @@ npcf:register_npc("npcf_builder:npc" ,{ reset_build(self) 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}) else diff --git a/npcf_guard/init.lua b/npcf_guard/init.lua index 319f989..0b66ef8 100644 --- a/npcf_guard/init.lua +++ b/npcf_guard/init.lua @@ -102,11 +102,12 @@ npcf:register_npc("npcf_guard:npc", { end, on_step = function(self, dtime) local control = npcf.control_framework.getControl(self) + local pos = control.pos if self.timer > 1 then 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 + 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 @@ -163,7 +164,7 @@ npcf:register_npc("npcf_guard:npc", { 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)) else @@ -180,7 +181,7 @@ 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) else @@ -191,8 +192,8 @@ npcf:register_npc("npcf_guard: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)) else From cf69529a55be2cbd766b2aede03330bfa0f01714 Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 08:46:01 +0100 Subject: [PATCH 2/8] disable debug messages --- npcf/control.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/npcf/control.lua b/npcf/control.lua index 4900972..28ea2b3 100644 --- a/npcf/control.lua +++ b/npcf/control.lua @@ -202,7 +202,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 @@ -266,9 +265,9 @@ function functions.get_path(control, pos) 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)) + --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)) + --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 @@ -289,7 +288,7 @@ function functions.check_for_stuck(control) 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)) + --print("big-jump teleport to "..minetest.pos_to_string(teleport_dest).." for target "..minetest.pos_to_string(control.target_pos)) end end @@ -302,7 +301,7 @@ function functions.check_for_stuck(control) 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") + --print("Stuck") if control.walk_param.teleport_on_stuck == true then local teleport_dest if vector.distance(control.pos, control.target_pos) > 5 then From a629c0e8dd65173015877c63eae42ceee59b9056 Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 08:50:59 +0100 Subject: [PATCH 3/8] Added the control framework to the npc framework --- README.md | 63 +++++++++ npcf/control.lua | 327 +++++++++++++++++++++++++++++++++++++++++++++++ npcf/npcf.lua | 8 +- 3 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 npcf/control.lua diff --git a/README.md b/README.md index ee3d044..b7a82c9 100644 --- a/README.md +++ b/README.md @@ -372,3 +372,66 @@ 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 +---- +## Methods +### npcf.control_framework.getControl(npc_ref) + +Constructor for the control object. Returns the reference. +Note, the framework will be activated for NPC on first usage. + +### control:stay() +Stop walking, stand up + +### control:look_to(pos) +Look (set yaw) to direction of position pos + +### control:sit() +Stop walking and sit down + +### control:lay() +Stop walking and lay down + +### control:mine() +Begin the mining / digging / attacking animation + +### control:mine_stop() +Stop the mining / digging / attacking animation + +### control: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() +Stay and forgot about the destination + +### control:set_walk_parameter(parameter) +key-value table to change the walking path determination parameter + + - find_path + - true (default): the minetest.find_path is used to find the way + - false: directly way to destination is used + + - find_path_fallback + - true (default): use directly way if no path found + + - fuzzy_destination + - true (default): try to find a walkable place nearly destination to get beter results with strict minetest.find_path + + - fuzzy_destination_distance + - if fuzzy_destination is enabled the fuzzy tolerance in nodes. Default 2 (higher value => more nodes read) + + - find_path_max_distance + technically setting to limit minetest.find_path distances (save performance) default is 20 + + - teleport_on_stuck + true: if enabled the NPC uses small teleports if stuck detected (destination not reachable). + Using this option the NPC is able to reach any place + 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 diff --git a/npcf/control.lua b/npcf/control.lua new file mode 100644 index 0000000..28ea2b3 --- /dev/null +++ b/npcf/control.lua @@ -0,0 +1,327 @@ +-- NPC framework navigation control object prototype +local control_proto = { + is_mining = false, + speed = 0, + target_pos = nil, + _path = nil, + _npc = nil, + _state = NPCF_ANIM_STAND, + _step_timer = 0, + walk_param = { + find_path = true, + find_path_fallback = true, + find_path_max_distance = 20, + fuzzy_destination = true, + fuzzy_destination_distance = 5, + teleport_on_stuck = false, + } +} + +-- navigation control framework +local control_framework = { + control_proto = control_proto, + functions = {}, + getControl = function(npc) + local control + if npc._control then + control = npc._control + else + control = npcf.deepcopy(control_proto) + control._npc = npc + npc._control = control + 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 + end + return control + end +} +local functions = control_framework.functions + + +-- Stop walking and stand up +function control_proto:stay() + self.speed = 0 + self._state = NPCF_ANIM_STAND +end + +-- Stay and forgot about the way +function control_proto:stop() + self:stay() + self._path = nil + self._target_pos_bak = nil + self.target_pos = nil + self._last_distance = nil +end + +-- look to position +function control_proto:look_to(pos) + self.yaw = npcf:get_face_direction(self.pos, pos) +end + +-- Stop walking and sitting down +function control_proto:sit() + self.speed = 0 + self.is_mining = false + self._state = NPCF_ANIM_SIT +end + +-- Stop walking and lay +function control_proto:lay() + self.speed = 0 + self.is_mining = false + self._state = NPCF_ANIM_LAY +end + +-- Start mining +function control_proto:mine() + self.is_mining = true +end + +-- Stop mining +function control_proto:mine_stop() + self.is_mining = false +end + +-- Change default parameters for walking +function control_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) + if param then + self:set_walk_parameter(param) + end + self._target_pos_bak = self.target_pos + self.target_pos = pos + self.speed = speed + if self.walk_param.find_path == true then + self._path = functions.get_path(self, pos) + else + self._path = { pos } + self._path_used = false + end + + if self._path == nil then + self:stop() + self:look_to(pos) + else + self._walk_started = true + end +end + +-- do a walking step +function control_proto:_do_control_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) + self._step_init_done = false + + functions.check_for_stuck(self) + + -- check path + if self.speed > 0 then + if not self._path or not self._path[1] then + self:stop() + else + local a = table.copy(self.pos) + a.y = 0 + local b = {x=self._path[1].x, y=0 ,z=self._path[1].z} + --print(minetest.pos_to_string(self.pos), minetest.pos_to_string(self._path[1]), vector.distance(a, b),minetest.pos_to_string(self._npc.object:getpos())) + --if self._path[2] then print(minetest.pos_to_string(self._path[2])) end + + if vector.distance(a, b) < 0.4 + or (self._path[2] and vector.distance(self.pos, self._path[2]) < vector.distance(self._path[1], self._path[2])) then + if self._path[2] then + table.remove(self._path, 1) + self._walk_started = true + else + self:stop() + end + end + end + end + -- check/set yaw + if self._path and self._path[1] then + self.yaw = npcf:get_face_direction(self.pos, self._path[1]) + end + self._npc.object:setyaw(self.yaw) + + -- check/set animation + if self.is_mining then + if self.speed == 0 then + self._state = NPCF_ANIM_MINE + else + self._state = NPCF_ANIM_WALK_MINE + end + else + if self.speed == 0 then + if self._state ~= NPCF_ANIM_SIT and + self._state ~= NPCF_ANIM_LAY then + self._state = NPCF_ANIM_STAND + end + else + self._state = NPCF_ANIM_WALK + end + end + npcf:set_animation(self._npc, self._state) + + -- check for current environment + local nodepos = table.copy(self.pos) + local node = {} + nodepos.y = nodepos.y - 0.5 + for i = -1, 1 do + node[i] = minetest.get_node(nodepos) + nodepos.y = nodepos.y + 1 + end + if string.find(node[-1].name, "^default:water") then + self.acceleration = {x=0, y=-4, z=0} + self._npc.object:setacceleration(self.acceleration) + -- we are walking in water + if string.find(node[0].name, "^default:water") or + string.find(node[1].name, "^default:water") then + -- we are under water. sink if target bellow the current position. otherwise swim up + if not self._path[1] or self._path[1].y > self.pos.y then + self.velocity.y = 3 + end + end + elseif minetest.find_node_near(self.pos, 2, {"group:water"}) then + -- Light-footed near water + self.acceleration = {x=0, y=-1, z=0} + 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 + -- jump if in catched in walkable node + self.velocity.y = 3 + else + -- walking + self.acceleration = {x=0, y=-10, z=0} + self._npc.object:setacceleration(self.acceleration) + end + + --check/set velocity + self.velocity = npcf:get_walk_velocity(self.speed, self.velocity.y, self.yaw) + self._npc.object:setvelocity(self.velocity) +end + +--------------------------------------------------------------- +-- define framework functions internally used +--------------------------------------------------------------- +function functions.get_walkable_pos(pos, dist) + local destpos + local rpos = vector.round(pos) + for y = rpos.y+dist-1, rpos.y-dist-1, -1 do + for x = rpos.x-dist, rpos.x+dist do + for z = rpos.z-dist, rpos.z+dist do + local p = {x=x, y=y, z=z} + local node = minetest.get_node(p) + local nodedef = minetest.registered_nodes[node.name] + if not (node.name == "air" or nodedef and (nodedef.walkable == false or nodedef.drawtype == "airlike")) then + p.y = p.y +1 + local node = minetest.get_node(p) + local nodedef = minetest.registered_nodes[node.name] + if node.name == "air" or nodedef and (nodedef.walkable == false or nodedef.drawtype == "airlike") then + if destpos == nil or vector.distance(p, pos) < vector.distance(destpos, pos) then + destpos = p + end + end + end + end + end + end + 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 diff --git a/npcf/npcf.lua b/npcf/npcf.lua index a4391b1..4c8829c 100644 --- a/npcf/npcf.lua +++ b/npcf/npcf.lua @@ -72,7 +72,10 @@ npcf = { }, animation_state = 0, animation_speed = 30, - } + }, + -- control functions + control_framework = dofile(NPCF_MODPATH.."/control.lua"), + deepcopy = deepcopy } -- Create NPC object instance @@ -239,6 +242,9 @@ 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) + end end else if type(def.on_activate) == "function" then From 3005342c828eac5101515783eb69ee4750f6252c Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 09:00:47 +0100 Subject: [PATCH 4/8] npcf_guard: do'nt get the control if not needed But it does not differnce because the framework get it in this case, but cleaner to read code --- npcf_guard/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/npcf_guard/init.lua b/npcf_guard/init.lua index 0b66ef8..400812f 100644 --- a/npcf_guard/init.lua +++ b/npcf_guard/init.lua @@ -101,9 +101,9 @@ npcf:register_npc("npcf_guard:npc", { end end, on_step = function(self, dtime) - local control = npcf.control_framework.getControl(self) - local pos = control.pos if self.timer > 1 then + local control = npcf.control_framework.getControl(self) + local pos = control.pos local target = {object=nil, distance=0} local min_dist = 1000 control:mine_stop() From 268fe91bf8dd32c6e2350e3ac6d514882521f86f Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 20:54:52 +0100 Subject: [PATCH 5/8] control framework renamed to movement framework --- README.md | 44 +++--- npcf/{control.lua => movement.lua} | 223 +++++++++++++++-------------- npcf/npcf.lua | 8 +- 3 files changed, 146 insertions(+), 129 deletions(-) rename npcf/{control.lua => movement.lua} (66%) diff --git a/README.md b/README.md index b7a82c9..c01bc70 100644 --- a/README.md +++ b/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 diff --git a/npcf/control.lua b/npcf/movement.lua similarity index 66% rename from npcf/control.lua rename to npcf/movement.lua index 28ea2b3..645da8c 100644 --- a/npcf/control.lua +++ b/npcf/movement.lua @@ -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 diff --git a/npcf/npcf.lua b/npcf/npcf.lua index 4c8829c..f80bc1c 100644 --- a/npcf/npcf.lua +++ b/npcf/npcf.lua @@ -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 From eb483cc135709f593c63ac0948a86f1a9c8951d1 Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 08:59:05 +0100 Subject: [PATCH 6/8] porting npcf_guard to control framework --- npcf_guard/init.lua | 59 +++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/npcf_guard/init.lua b/npcf_guard/init.lua index b9d3d56..400812f 100644 --- a/npcf_guard/init.lua +++ b/npcf_guard/init.lua @@ -102,15 +102,11 @@ npcf:register_npc("npcf_guard:npc", { end, on_step = function(self, dtime) if self.timer > 1 then - local pos = self.object:getpos() - local yaw = self.object:getyaw() - local state = NPCF_ANIM_STAND + local control = npcf.control_framework.getControl(self) + local pos = control.pos local target = {object=nil, distance=0} local min_dist = 1000 - local speed = 0 - local acceleration = {x=0, y=-10, z=0} - local velocity = self.object:getvelocity() - local attacking = false + control:mine_stop() for _,object in ipairs(minetest.get_objects_inside_radius(pos, TARGET_RADIUS)) do local to_target = false if object:is_player() then @@ -147,19 +143,10 @@ npcf:register_npc("npcf_guard:npc", { end end if target.object then - yaw = npcf:get_face_direction(pos, target.object:getpos()) if target.distance < 3 then - attacking = true - state = NPCF_ANIM_MINE - end - if target.distance > 2 then - speed = get_speed(target.distance) * 1.1 - state = NPCF_ANIM_WALK - if attacking == true then - state = NPCF_ANIM_WALK_MINE - end - end - if attacking == true then + control:mine() + control:stay() + control: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 @@ -169,16 +156,21 @@ npcf:register_npc("npcf_guard:npc", { end target.object:punch(self.object, self.var.timer, tool_caps) end + if target.distance > 2 then + local speed = get_speed(target.distance) * 1.1 + control: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 - yaw = npcf:get_face_direction(pos, player:getpos()) local p = player:getpos() local distance = vector.distance(pos, {x=p.x, y=pos.y, z=p.z}) if distance > 3 then - speed = get_speed(distance) - state = NPCF_ANIM_WALK + control:walk(p, get_speed(distance)) + else + control:stay() end + control:mine_stop() end elseif self.metadata.patrol == "true" then self.var.rest_timer = self.var.rest_timer + self.timer @@ -191,38 +183,25 @@ npcf:register_npc("npcf_guard:npc", { if patrol_pos then local distance = vector.distance(pos, patrol_pos) if distance > 1 then - yaw = npcf:get_face_direction(pos, patrol_pos) - speed = PATROL_SPEED - state = NPCF_ANIM_WALK + control:walk(patrol_pos, PATROL_SPEED) else self.object:setpos(patrol_pos) + control:stay() self.metadata.patrol_index = index self.var.rest_timer = 0 end end end elseif vector.equals(pos, self.origin.pos) == false then - yaw = npcf:get_face_direction(pos, self.origin.pos) local distance = vector.distance(pos, self.origin.pos) if distance > 1 then - speed = get_speed(distance) - state = NPCF_ANIM_WALK + control:walk(self.origin.pos, get_speed(distance)) else self.object:setpos(self.origin.pos) - yaw = self.origin.yaw + control.look_to(self.origin.pos) + control:stay() end end - local node = minetest.get_node(pos) - if string.find(node.name, "^default:water") then - acceleration = {x=0, y=-4, z=0} - velocity = {x=0, y=3, z=0} - elseif minetest.find_node_near(pos, 2, {"group:water"}) then - acceleration = {x=0, y=-1, z=0} - end - self.object:setvelocity(npcf:get_walk_velocity(speed, velocity.y, yaw)) - self.object:setacceleration(acceleration) - self.object:setyaw(yaw) - npcf:set_animation(self, state) self.timer = 0 end end, From b97cccbd2ed86e6c54c51cfa9ade3b39eb1799d4 Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Thu, 19 Jan 2017 20:51:06 +0100 Subject: [PATCH 7/8] control framework renamed to movement framework --- npcf_guard/init.lua | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/npcf_guard/init.lua b/npcf_guard/init.lua index 400812f..46852d1 100644 --- a/npcf_guard/init.lua +++ b/npcf_guard/init.lua @@ -102,11 +102,11 @@ npcf:register_npc("npcf_guard:npc", { end, on_step = function(self, dtime) if self.timer > 1 then - local control = npcf.control_framework.getControl(self) - local pos = control.pos + 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() + 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 @@ -144,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 @@ -158,7 +158,7 @@ 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) @@ -166,11 +166,11 @@ npcf:register_npc("npcf_guard:npc", { local p = player:getpos() 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 @@ -183,10 +183,9 @@ npcf:register_npc("npcf_guard:npc", { if patrol_pos then 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 @@ -195,11 +194,11 @@ npcf:register_npc("npcf_guard:npc", { 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 From 2d35a15db108e684defb13d7e946ad355d02ae09 Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Sun, 22 Jan 2017 13:07:10 +0100 Subject: [PATCH 8/8] control.lua removed --- npcf/control.lua | 327 ----------------------------------------------- 1 file changed, 327 deletions(-) delete mode 100644 npcf/control.lua diff --git a/npcf/control.lua b/npcf/control.lua deleted file mode 100644 index 28ea2b3..0000000 --- a/npcf/control.lua +++ /dev/null @@ -1,327 +0,0 @@ --- NPC framework navigation control object prototype -local control_proto = { - is_mining = false, - speed = 0, - target_pos = nil, - _path = nil, - _npc = nil, - _state = NPCF_ANIM_STAND, - _step_timer = 0, - walk_param = { - find_path = true, - find_path_fallback = true, - find_path_max_distance = 20, - fuzzy_destination = true, - fuzzy_destination_distance = 5, - teleport_on_stuck = false, - } -} - --- navigation control framework -local control_framework = { - control_proto = control_proto, - functions = {}, - getControl = function(npc) - local control - if npc._control then - control = npc._control - else - control = npcf.deepcopy(control_proto) - control._npc = npc - npc._control = control - 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 - end - return control - end -} -local functions = control_framework.functions - - --- Stop walking and stand up -function control_proto:stay() - self.speed = 0 - self._state = NPCF_ANIM_STAND -end - --- Stay and forgot about the way -function control_proto:stop() - self:stay() - self._path = nil - self._target_pos_bak = nil - self.target_pos = nil - self._last_distance = nil -end - --- look to position -function control_proto:look_to(pos) - self.yaw = npcf:get_face_direction(self.pos, pos) -end - --- Stop walking and sitting down -function control_proto:sit() - self.speed = 0 - self.is_mining = false - self._state = NPCF_ANIM_SIT -end - --- Stop walking and lay -function control_proto:lay() - self.speed = 0 - self.is_mining = false - self._state = NPCF_ANIM_LAY -end - --- Start mining -function control_proto:mine() - self.is_mining = true -end - --- Stop mining -function control_proto:mine_stop() - self.is_mining = false -end - --- Change default parameters for walking -function control_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) - if param then - self:set_walk_parameter(param) - end - self._target_pos_bak = self.target_pos - self.target_pos = pos - self.speed = speed - if self.walk_param.find_path == true then - self._path = functions.get_path(self, pos) - else - self._path = { pos } - self._path_used = false - end - - if self._path == nil then - self:stop() - self:look_to(pos) - else - self._walk_started = true - end -end - --- do a walking step -function control_proto:_do_control_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) - self._step_init_done = false - - functions.check_for_stuck(self) - - -- check path - if self.speed > 0 then - if not self._path or not self._path[1] then - self:stop() - else - local a = table.copy(self.pos) - a.y = 0 - local b = {x=self._path[1].x, y=0 ,z=self._path[1].z} - --print(minetest.pos_to_string(self.pos), minetest.pos_to_string(self._path[1]), vector.distance(a, b),minetest.pos_to_string(self._npc.object:getpos())) - --if self._path[2] then print(minetest.pos_to_string(self._path[2])) end - - if vector.distance(a, b) < 0.4 - or (self._path[2] and vector.distance(self.pos, self._path[2]) < vector.distance(self._path[1], self._path[2])) then - if self._path[2] then - table.remove(self._path, 1) - self._walk_started = true - else - self:stop() - end - end - end - end - -- check/set yaw - if self._path and self._path[1] then - self.yaw = npcf:get_face_direction(self.pos, self._path[1]) - end - self._npc.object:setyaw(self.yaw) - - -- check/set animation - if self.is_mining then - if self.speed == 0 then - self._state = NPCF_ANIM_MINE - else - self._state = NPCF_ANIM_WALK_MINE - end - else - if self.speed == 0 then - if self._state ~= NPCF_ANIM_SIT and - self._state ~= NPCF_ANIM_LAY then - self._state = NPCF_ANIM_STAND - end - else - self._state = NPCF_ANIM_WALK - end - end - npcf:set_animation(self._npc, self._state) - - -- check for current environment - local nodepos = table.copy(self.pos) - local node = {} - nodepos.y = nodepos.y - 0.5 - for i = -1, 1 do - node[i] = minetest.get_node(nodepos) - nodepos.y = nodepos.y + 1 - end - if string.find(node[-1].name, "^default:water") then - self.acceleration = {x=0, y=-4, z=0} - self._npc.object:setacceleration(self.acceleration) - -- we are walking in water - if string.find(node[0].name, "^default:water") or - string.find(node[1].name, "^default:water") then - -- we are under water. sink if target bellow the current position. otherwise swim up - if not self._path[1] or self._path[1].y > self.pos.y then - self.velocity.y = 3 - end - end - elseif minetest.find_node_near(self.pos, 2, {"group:water"}) then - -- Light-footed near water - self.acceleration = {x=0, y=-1, z=0} - 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 - -- jump if in catched in walkable node - self.velocity.y = 3 - else - -- walking - self.acceleration = {x=0, y=-10, z=0} - self._npc.object:setacceleration(self.acceleration) - end - - --check/set velocity - self.velocity = npcf:get_walk_velocity(self.speed, self.velocity.y, self.yaw) - self._npc.object:setvelocity(self.velocity) -end - ---------------------------------------------------------------- --- define framework functions internally used ---------------------------------------------------------------- -function functions.get_walkable_pos(pos, dist) - local destpos - local rpos = vector.round(pos) - for y = rpos.y+dist-1, rpos.y-dist-1, -1 do - for x = rpos.x-dist, rpos.x+dist do - for z = rpos.z-dist, rpos.z+dist do - local p = {x=x, y=y, z=z} - local node = minetest.get_node(p) - local nodedef = minetest.registered_nodes[node.name] - if not (node.name == "air" or nodedef and (nodedef.walkable == false or nodedef.drawtype == "airlike")) then - p.y = p.y +1 - local node = minetest.get_node(p) - local nodedef = minetest.registered_nodes[node.name] - if node.name == "air" or nodedef and (nodedef.walkable == false or nodedef.drawtype == "airlike") then - if destpos == nil or vector.distance(p, pos) < vector.distance(destpos, pos) then - destpos = p - end - end - end - end - end - end - 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