476 lines
13 KiB
Lua
476 lines
13 KiB
Lua
|
|
-- Imported and modified API functions from Mobs_Redo
|
|
-- WIP; pathfinding is not functional
|
|
|
|
-- Localize math functions
|
|
local pi = math.pi
|
|
local square = math.sqrt
|
|
local sin = math.sin
|
|
local cos = math.cos
|
|
local abs = math.abs
|
|
local min = math.min
|
|
local max = math.max
|
|
local atann = math.atan
|
|
local random = math.random
|
|
local floor = math.floor
|
|
local atan = function(x)
|
|
if not x or x ~= x then
|
|
--error("atan bassed NaN")
|
|
return 0
|
|
else
|
|
return atann(x)
|
|
end
|
|
end
|
|
|
|
local helper = adaptive_ai.helper
|
|
local set_yaw = helper.mobs.set_yaw
|
|
local do_jump = helper.mobs.do_jump
|
|
local set_velocity = helper.mobs.set_velocity
|
|
local is_at_cliff = helper.mobs.is_at_cliff
|
|
local flight_check = helper.mobs.flight_check
|
|
local get_distance = helper.mobs.get_distance
|
|
local mob_sound = helper.mobs.mob_sound
|
|
local do_env_damage = helper.mobs.do_env_damage
|
|
local replace = helper.mobs.replace
|
|
local flop = helper.mobs.flop
|
|
local runaway_from = helper.mobs.runaway_from
|
|
local set_animation = helper.mobs.set_animation
|
|
|
|
local mobs_griefing = helper.mobs.mobs_griefing
|
|
local los_switcher = false
|
|
local height_switcher = false
|
|
|
|
local chat_log = adaptive_ai.chat_log
|
|
|
|
-- Edited from mob_redo's smart_mobs pathfinding function
|
|
-- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3
|
|
local ai_pathfinding = function(self, s, p, dist, dtime)
|
|
chat_log("pathfinding tete")
|
|
local s1 = self.path.lastpos
|
|
local target_pos = self.target.pos
|
|
|
|
-- is it becoming stuck?
|
|
if abs(s1.x - s.x) + abs(s1.z - s.z) < .5 then
|
|
self.path.stuck_timer = self.path.stuck_timer + dtime
|
|
else
|
|
self.path.stuck_timer = 0
|
|
end
|
|
|
|
self.path.lastpos = {x = s.x, y = s.y, z = s.z}
|
|
|
|
local use_pathfind = false
|
|
local has_lineofsight = minetest.line_of_sight(
|
|
{x = s.x, y = (s.y) + .5, z = s.z},
|
|
{x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z},
|
|
.2)
|
|
|
|
-- im stuck, search for path
|
|
if not has_lineofsight then
|
|
if los_switcher == true then
|
|
use_pathfind = true
|
|
los_switcher = false
|
|
end -- cannot see target!
|
|
else
|
|
if los_switcher == false then
|
|
los_switcher = true
|
|
use_pathfind = false
|
|
|
|
minetest.after(1, function(self)
|
|
if has_lineofsight then self.path.following = false end
|
|
end, self)
|
|
end -- can see target!
|
|
end
|
|
|
|
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
|
use_pathfind = true
|
|
self.path.stuck_timer = 0
|
|
|
|
minetest.after(1, function(self)
|
|
if has_lineofsight then self.path.following = false end
|
|
end, self)
|
|
end
|
|
|
|
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
|
use_pathfind = true
|
|
self.path.stuck_timer = 0
|
|
|
|
minetest.after(1, function(self)
|
|
if has_lineofsight then self.path.following = false end
|
|
end, self)
|
|
end
|
|
|
|
if abs(vector.subtract(s,target_pos).y) > self.stepheight then
|
|
if height_switcher then
|
|
use_pathfind = true
|
|
height_switcher = false
|
|
end
|
|
else
|
|
if not height_switcher then
|
|
use_pathfind = false
|
|
height_switcher = true
|
|
end
|
|
end
|
|
|
|
if use_pathfind then
|
|
-- lets try find a path, first take care of positions
|
|
-- since pathfinder is very sensitive
|
|
local sheight = self.collisionbox[5] - self.collisionbox[2]
|
|
|
|
-- round position to center of node to avoid stuck in walls
|
|
-- also adjust height for player models!
|
|
s.x = floor(s.x + 0.5)
|
|
-- s.y = floor(s.y + 0.5) - sheight
|
|
s.z = floor(s.z + 0.5)
|
|
|
|
local ssight, sground = minetest.line_of_sight(s, {x = s.x, y = s.y - 4, z = s.z}, 1)
|
|
|
|
-- determine node above ground
|
|
if not ssight then
|
|
s.y = sground.y + 1
|
|
end
|
|
|
|
local p1 = self.target.pos
|
|
p1.x, p1.y, p1.z = floor(p1.x + 0.5), floor(p1.y + 0.5), floor(p1.z + 0.5)
|
|
|
|
local dropheight = 6
|
|
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
|
|
|
self.path.way = minetest.find_path(s, p1, self.view_range, self.stepheight, dropheight, "A*")
|
|
self.state = "move"
|
|
self.target.dir = "towards"
|
|
|
|
-- no path found, try something else
|
|
if not self.path.way then
|
|
self.path.following = false
|
|
|
|
-- lets make way by digging/building if not accessible
|
|
if self.pathfinding == 2 and mobs_griefing then
|
|
|
|
-- is player higher than mob?
|
|
if s.y < p1.y then
|
|
|
|
-- build upwards
|
|
if not minetest.is_protected(s, "") then
|
|
local ndef1 = minetest.registered_nodes[self.standing_in]
|
|
|
|
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
|
|
minetest.set_node(s, {name = mobs.fallback_node})
|
|
end
|
|
end
|
|
|
|
local sheight = math.ceil(self.collisionbox[5]) + 1
|
|
|
|
-- assume mob is 2 blocks high so it digs above its head
|
|
s.y = s.y + sheight
|
|
|
|
-- remove one block above to make room to jump
|
|
if not minetest.is_protected(s, "") then
|
|
local node1 = node_ok(s, "air").name
|
|
local ndef1 = minetest.registered_nodes[node1]
|
|
|
|
if node1 ~= "air"
|
|
and node1 ~= "ignore"
|
|
and ndef1
|
|
and not ndef1.groups.level
|
|
and not ndef1.groups.unbreakable
|
|
and not ndef1.groups.liquid then
|
|
|
|
minetest.set_node(s, {name = "air"})
|
|
minetest.add_item(s, ItemStack(node1))
|
|
end
|
|
end
|
|
|
|
s.y = s.y - sheight
|
|
self.object:setpos({x = s.x, y = s.y + 2, z = s.z})
|
|
|
|
else -- dig 2 blocks to make door toward player direction
|
|
local yaw1 = self.object:get_yaw() + pi / 2
|
|
local p1 = {x = s.x + cos(yaw1), y = s.y, z = s.z + sin(yaw1)}
|
|
|
|
if not minetest.is_protected(p1, "") then
|
|
local node1 = node_ok(p1, "air").name
|
|
local ndef1 = minetest.registered_nodes[node1]
|
|
|
|
if node1 ~= "air"
|
|
and node1 ~= "ignore"
|
|
and ndef1
|
|
and not ndef1.groups.level
|
|
and not ndef1.groups.unbreakable
|
|
and not ndef1.groups.liquid then
|
|
|
|
minetest.add_item(p1, ItemStack(node1))
|
|
minetest.set_node(p1, {name = "air"})
|
|
end
|
|
|
|
p1.y = p1.y + 1
|
|
node1 = node_ok(p1, "air").name
|
|
ndef1 = minetest.registered_nodes[node1]
|
|
|
|
if node1 ~= "air"
|
|
and node1 ~= "ignore"
|
|
and ndef1
|
|
and not ndef1.groups.level
|
|
and not ndef1.groups.unbreakable
|
|
and not ndef1.groups.liquid then
|
|
|
|
minetest.add_item(p1, ItemStack(node1))
|
|
minetest.set_node(p1, {name = "air"})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- will try again in 2 second
|
|
self.path.stuck_timer = stuck_timeout - 2
|
|
|
|
-- frustration! cant find the damn path :(
|
|
mob_sound(self, self.sounds.random)
|
|
else
|
|
-- yay I found path
|
|
mob_sound(self, self.sounds.war_cry)
|
|
set_velocity(self, self.walk_velocity)
|
|
|
|
-- follow path now that I have it
|
|
self.path.following = true
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Pieced together from mob_redo's state management functions
|
|
-- self.target.pos
|
|
-- self.target.dir = "towards" or "away"
|
|
local ai_manager = function(self, dtime)
|
|
if self.action_managers then
|
|
-- there's a manager function per action
|
|
if self.action then
|
|
--chat_log("action managers in action")
|
|
self.action_managers[self.action](self, dtime)
|
|
end
|
|
end
|
|
|
|
--chat_log(self.state == "manager on")
|
|
if self.target and self.target.obj then
|
|
self.target.pos = self.target.obj:get_pos()
|
|
end
|
|
local yaw = self.object:get_yaw() or 0
|
|
local s = self.object:get_pos()
|
|
local lp = nil
|
|
|
|
if self.state == "stand" then
|
|
--chat_log("I am idle standing")
|
|
if random(1, 4) == 1 then
|
|
local objs = minetest.get_objects_inside_radius(s, 3)
|
|
for n = 1, #objs do
|
|
if objs[n]:is_player() then
|
|
lp = objs[n]:get_pos()
|
|
break
|
|
end
|
|
end
|
|
|
|
-- look at any players nearby, otherwise turn randomly
|
|
if lp then
|
|
local vec = {x = lp.x - s.x, z = lp.z - s.z}
|
|
yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate
|
|
if lp.x > s.x then yaw = yaw + pi end
|
|
else
|
|
yaw = yaw + random(-0.5, 0.5)
|
|
end
|
|
|
|
yaw = set_yaw(self, yaw, 8)
|
|
end
|
|
|
|
if self.walk_chance ~= 0
|
|
and self.facing_fence ~= true
|
|
and random(1, 100) <= self.walk_chance
|
|
and is_at_cliff(self) == false then
|
|
|
|
set_velocity(self, self.walk_velocity)
|
|
self.state = "wander"
|
|
set_animation(self, "walk")
|
|
else
|
|
set_velocity(self, 0)
|
|
set_animation(self, "stand")
|
|
end
|
|
elseif self.state == "wander" then
|
|
--chat_log("I am idle wandering")
|
|
-- is there something I need to avoid?
|
|
local avoid = {}
|
|
if self.water_damage > 0 then table.insert(avoid, "group:water") end
|
|
if self.lava_damage > 0 then table.insert(avoid, "group:lava") end
|
|
|
|
if #avoid > 0 then lp = minetest.find_node_near(s, 1, avoid) end
|
|
|
|
if lp then
|
|
-- if mob in water or lava then look for land
|
|
if (self.lava_damage and minetest.registered_nodes[self.standing_in].groups.lava)
|
|
or (self.water_damage and minetest.registered_nodes[self.standing_in].groups.water) then
|
|
lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone", "group:sand", node_ice, node_snowblock})
|
|
|
|
-- did we find land?
|
|
if lp then
|
|
local vec = {x = lp.x - s.x, z = lp.z - s.z}
|
|
yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate
|
|
|
|
if lp.x > s.x then yaw = yaw + pi end
|
|
|
|
-- look towards land and jump/move in that direction
|
|
yaw = set_yaw(self, yaw, 6)
|
|
do_jump(self)
|
|
set_velocity(self, self.walk_velocity)
|
|
else
|
|
yaw = yaw + random(-0.5, 0.5)
|
|
end
|
|
|
|
else
|
|
local vec = {x = lp.x - s.x, z = lp.z - s.z}
|
|
yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate
|
|
if lp.x > s.x then yaw = yaw + pi end
|
|
end
|
|
|
|
yaw = set_yaw(self, yaw, 8)
|
|
|
|
-- otherwise randomly turn
|
|
elseif random(1, 100) <= 30 then
|
|
yaw = yaw + random(-0.5, 0.5)
|
|
yaw = set_yaw(self, yaw, 8)
|
|
end
|
|
|
|
-- stand for great fall in front
|
|
local temp_is_cliff = is_at_cliff(self)
|
|
|
|
if self.facing_fence == true or temp_is_cliff or random(1, 100) <= 30 then
|
|
set_velocity(self, 0)
|
|
self.state = "stand"
|
|
set_animation(self, "stand")
|
|
else
|
|
set_velocity(self, self.walk_velocity)
|
|
|
|
if flight_check(self)
|
|
and self.animation
|
|
and self.animation.fly_start and self.animation.fly_end then
|
|
set_animation(self, "fly")
|
|
else
|
|
set_animation(self, "walk")
|
|
end
|
|
end
|
|
elseif self.state == "runaway" then
|
|
--chat_log("I am moving away")
|
|
self.runaway_timer = self.runaway_timer + 1
|
|
|
|
-- stop after 5 seconds or when at cliff
|
|
if self.runaway_timer > 5
|
|
or is_at_cliff(self) then
|
|
self.runaway_timer = 0
|
|
set_velocity(self, 0)
|
|
self.state = "stand"
|
|
set_animation(self, "stand")
|
|
self.runaway_timer = 0
|
|
else
|
|
set_velocity(self, self.run_velocity)
|
|
set_animation(self, "walk")
|
|
end
|
|
elseif self.state == "move" then
|
|
-- is there something I need to avoid?
|
|
if self.water_damage > 0
|
|
and self.lava_damage > 0 then
|
|
lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"})
|
|
elseif self.water_damage > 0 then
|
|
lp = minetest.find_node_near(s, 1, {"group:water"})
|
|
elseif self.lava_damage > 0 then
|
|
lp = minetest.find_node_near(s, 1, {"group:lava"})
|
|
end
|
|
|
|
if lp then
|
|
-- if mob in water or lava then look for land
|
|
if (self.lava_damage
|
|
and minetest.registered_nodes[self.standing_in].groups.lava)
|
|
or (self.water_damage
|
|
and minetest.registered_nodes[self.standing_in].groups.water) then
|
|
|
|
lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone",
|
|
"group:sand", node_ice, node_snowblock})
|
|
|
|
-- did we find land?
|
|
if lp then
|
|
local vec = {
|
|
x = lp.x - s.x,
|
|
z = lp.z - s.z
|
|
}
|
|
yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate
|
|
|
|
if lp.x > s.x then yaw = yaw + pi end
|
|
|
|
-- look towards land and jump/move in that direction
|
|
yaw = set_yaw(self, yaw, 6)
|
|
local jumped = do_jump(self)
|
|
if jumped then chat_log("Jumping while moving") end
|
|
set_velocity(self, self.walk_velocity)
|
|
else
|
|
yaw = yaw + random(-0.5, 0.5)
|
|
end
|
|
else
|
|
local vec = {
|
|
x = lp.x - s.x,
|
|
z = lp.z - s.z
|
|
}
|
|
|
|
yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate
|
|
|
|
if lp.x > s.x then yaw = yaw + pi end
|
|
end
|
|
|
|
yaw = set_yaw(self, yaw, 8)
|
|
end
|
|
|
|
-- stand for great fall in front
|
|
local temp_is_cliff = is_at_cliff(self)
|
|
|
|
if self.facing_fence == true
|
|
or temp_is_cliff then
|
|
set_velocity(self, 0)
|
|
self.state = "stand"
|
|
set_animation(self, "stand")
|
|
else
|
|
set_velocity(self, self.walk_velocity)
|
|
|
|
if flight_check(self)
|
|
and self.animation
|
|
and self.animation.fly_start
|
|
and self.animation.fly_end then
|
|
set_animation(self, "fly")
|
|
else
|
|
set_animation(self, "walk")
|
|
end
|
|
end
|
|
end
|
|
-- end ai_manager
|
|
end
|
|
|
|
local ai_on_step = function(self, dtime)
|
|
-- To do: add rest of mob_redo functionality, like:
|
|
-- Attacking
|
|
-- Follow
|
|
-- Etc
|
|
-- mob plays random sound at times
|
|
if random(1, 100) == 1 then
|
|
mob_sound(self, self.sounds.random)
|
|
end
|
|
|
|
-- environmental damage timer (every 1 second)
|
|
self.env_damage_timer = self.env_damage_timer + dtime
|
|
|
|
if (self.env_damage_timer > 1) then
|
|
self.env_damage_timer = 0
|
|
-- check for environmental damage (water, fire, lava etc.)
|
|
do_env_damage(self)
|
|
-- node replace check (cow eats grass etc.)
|
|
replace(self, pos)
|
|
end
|
|
--flop(self)
|
|
do_jump(self)
|
|
runaway_from(self)
|
|
end
|
|
|
|
adaptive_ai.ai_pathfinding = ai_pathfinding
|
|
adaptive_ai.ai_manager = ai_manager
|
|
adaptive_ai.ai_on_step = ai_on_step
|