water_life/behaviors.lua
2020-06-21 13:34:06 +02:00

555 lines
15 KiB
Lua

local abs = math.abs
local pi = math.pi
local floor = math.floor
local ceil = math.ceil
local sqrt = math.sqrt
local max = math.max
local min = math.min
local pow = math.pow
local sign = math.sign
local time = os.time
local rad = math.rad
local random = water_life.random
------------------
-- LQ behaviors --
------------------
function water_life.lq_dumbjump(self,height,anim)
anim = anim or 'stand'
local jump = true
local func=function(self)
local yaw = self.object:get_yaw()
if jump then
mobkit.animate(self,anim)
local dir = minetest.yaw_to_dir(yaw)
dir.y = -mobkit.gravity*sqrt((height+0.35)*2/-mobkit.gravity)
self.object:set_velocity(dir)
jump = false
else -- the eagle has landed
return true
end
end
mobkit.queue_low(self,func)
end
function water_life.lq_dumbwalk(self,dest,speed_factor)
local timer = 3 -- failsafe
speed_factor = speed_factor or 1
local func=function(self)
mobkit.animate(self,'walk')
timer = timer - self.dtime
if timer < 0 then return true end
local pos = mobkit.get_stand_pos(self)
local y = self.object:get_velocity().y
local dir = vector.normalize(vector.direction({x=pos.x,y=0,z=pos.z},
{x=dest.x,y=0,z=dest.z}))
dir = vector.multiply(dir,self.max_speed*speed_factor)
mobkit.turn2yaw(self,minetest.dir_to_yaw(dir))
dir.y = y
self.object:set_velocity(dir)
end
mobkit.queue_low(self,func)
end
------------------
-- HQ behaviors --
------------------
-- on land only, go to tgt and remove it
function water_life.hq_catch_drop(self,prty,tgt)
local func = function(self)
if self.isinliquid then return true end
if not tgt then return true end
if mobkit.is_queue_empty_low(self) then
local pos = mobkit.get_stand_pos(self)
local tpos = tgt:get_pos()
if pos and tpos then
local dist = vector.distance(pos,tpos)
if dist < 2 then
tgt:remove()
return true
else
if pos.y +0.5 >= tpos.y then
water_life.lq_dumbwalk(self,tpos,0.1)
else
water_life.lq_dumbjump(self,1)
end
end
else
return true
end
end
end
mobkit.queue_high(self,func,prty)
end
-- turn around 180degrees from tgtob and swim away until out of sight
function water_life.hq_swimfrom(self,prty,tgtobj,speed,outofsight)
local func = function(self)
if not outofsight then outofsight = self.view_range * 1.5 end
if not mobkit.is_alive(tgtobj) then return true end
local pos = mobkit.get_stand_pos(self)
local opos = tgtobj:get_pos()
local yaw = water_life.get_yaw_to_object(self,tgtobj) + math.rad(random(-30,30))+math.rad(180)
local distance = vector.distance(pos,opos)
if distance < outofsight then
local swimto, height = water_life.aqua_radar_dumb(pos,yaw,3)
if height and height > pos.y then
local vel = self.object:get_velocity()
vel.y = vel.y+0.1
self.object:set_velocity(vel)
end
mobkit.hq_aqua_turn(self,51,swimto,speed)
else
return true
end
--minetest.chat_send_all("angel= "..dump(yaw).." viewrange= "..dump(self.view_range).." distance= "..dump(vector.distance(pos,opos)))
end
mobkit.queue_high(self,func,prty)
end
-- same as mobkit.hq_aqua_turn but for large mobs
function water_life.big_hq_aqua_turn(self,prty,tyaw,speed)
local func = function(self)
if not speed then speed = 0.4 end
if speed < 0 then speed = speed * -1 end
local finished=mobkit.turn2yaw(self,tyaw,speed)
if finished then return true end
end
mobkit.queue_high(self,func,prty)
end
-- same as mobkit.hq_aqua_roam but for large mobs
function water_life.big_aqua_roam(self,prty,speed)
local tyaw = 0
local init = true
local prvscanpos = {x=0,y=0,z=0}
local center = self.object:get_pos()
local func = function(self)
if init then
mobkit.animate(self,'def')
init = false
end
local pos = mobkit.get_stand_pos(self)
local yaw = self.object:get_yaw()
local scanpos = mobkit.get_node_pos(mobkit.pos_translate2d(pos,yaw,speed))
if not vector.equals(prvscanpos,scanpos) then
prvscanpos=scanpos
local nyaw,height = water_life.aqua_radar_dumb(pos,yaw,speed,true)
if height and height > pos.y then
local vel = self.object:get_velocity()
vel.y = vel.y+0.1
self.object:set_velocity(vel)
end
if yaw ~= nyaw then
tyaw=nyaw
mobkit.hq_aqua_turn(self,prty+1,tyaw,speed)
return
end
end
if mobkit.timer(self,10) then
if vector.distance(pos,center) > water_life.abr*16*0.5 then
tyaw = minetest.dir_to_yaw(vector.direction(pos,{x=center.x+random()*10-5,y=center.y,z=center.z+random()*10-5}))
else
if random(10)>=9 then tyaw=tyaw+random()*pi - pi*0.5 end
end
end
if mobkit.timer(self,20) then mobkit.turn2yaw(self,tyaw,-1) end
--local yaw = self.object:get_yaw()
mobkit.go_forward_horizontal(self,speed)
end
mobkit.queue_high(self,func,prty)
end
-- this is the same as mobkit's, but allows movement in shallow water
function water_life.hq_aqua_roam(self,prty,speed)
local tyaw = 0
local init = true
local prvscanpos = {x=0,y=0,z=0}
local center = self.object:get_pos()
local func = function(self)
if init then
mobkit.animate(self,'def')
init = false
end
local pos = mobkit.get_stand_pos(self)
local yaw = self.object:get_yaw()
local scanpos = mobkit.get_node_pos(mobkit.pos_translate2d(pos,yaw,speed))
if not vector.equals(prvscanpos,scanpos) then
prvscanpos=scanpos
local nyaw,height = water_life.aqua_radar_dumb(pos,yaw,speed,true,true)
if height and height > pos.y then
local vel = self.object:get_velocity()
vel.y = vel.y+1
self.object:set_velocity(vel)
end
if yaw ~= nyaw then
tyaw=nyaw
mobkit.hq_aqua_turn(self,prty+1,tyaw,speed)
return
end
end
if mobkit.timer(self,1) then
if vector.distance(pos,center) > water_life.abr*16*0.5 then
tyaw = minetest.dir_to_yaw(vector.direction(pos,{x=center.x+random()*10-5,y=center.y,z=center.z+random()*10-5}))
else
if random(10)>=9 then tyaw=tyaw+random()*pi - pi*0.5 end
end
end
mobkit.turn2yaw(self,tyaw,3)
-- local yaw = self.object:get_yaw()
mobkit.go_forward_horizontal(self,speed)
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_attack(self,prty,tgtobj)
local func = function(self)
if self.isinliquid then return true end
if not mobkit.is_alive(tgtobj) then return true end
if mobkit.is_queue_empty_low(self) then
local pos = mobkit.get_stand_pos(self)
-- local tpos = tgtobj:get_pos()
local tpos = mobkit.get_stand_pos(tgtobj)
local dist = vector.distance(pos,tpos)
if dist > 3 then
return true
else
mobkit.lq_turn2pos(self,tpos)
local height = tgtobj:is_player() and 0.35 or tgtobj:get_luaentity().height*0.6
if tpos.y+height>pos.y then
mobkit.make_sound(self,"attack")
mobkit.lq_jumpattack(self,tpos.y+height-pos.y,tgtobj)
else
mobkit.lq_dumbwalk(self,mobkit.pos_shift(tpos,{x=random()-0.5,z=random()-0.5}))
end
end
end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_hunt(self,prty,tgtobj,lost)
if not lost then lost = self.view_range end
if water_life.leftorright() then mobkit.make_sound(self,"attack") end
local func = function(self)
if not mobkit.is_alive(tgtobj) then return true end
if self.isinliquid then return true end
if mobkit.is_queue_empty_low(self) and self.isonground then
local pos = mobkit.get_stand_pos(self)
local opos = tgtobj:get_pos()
local dist = vector.distance(pos,opos)
if mobkit.is_in_deep(tgtobj) then
return true --water_life.hq_water_attack(self,tgtobj,prty+1,7)
end
if dist > lost or math.abs(pos.y - opos.y) > 5 then
return true
elseif dist > 3 then
water_life.goto_next_waypoint(self,opos)
else
water_life.hq_attack(self,prty+1,tgtobj)
end
end
end
mobkit.queue_high(self,func,prty)
end
-- slowly roam on land, breaks are taken with max of 120 seconds
function water_life.hq_slow_roam(self,prty)
local func=function(self)
if self.isinliquid then return true end
if mobkit.is_queue_empty_low(self) and self.isonground then
local pos = mobkit.get_stand_pos(self)
local neighbor = random(8)
local height, tpos, liquidflag = mobkit.is_neighbor_node_reachable(self,neighbor)
if height and not liquidflag then mobkit.dumbstep(self,height,tpos,0.1,random(120)) end
end
end
mobkit.queue_high(self,func,prty)
end
--find any water nearby and go into it
function water_life.hq_go2water(self,prty)
local pos = mobkit.get_stand_pos(self)
local target = minetest.find_node_near(pos, self.view_range, {"group:water"})
--if target then water_life.temp_show(target,10,10) end
local func=function(self)
--minetest.chat_send_all(dump(vector.distance(pos,target)))
if self.isinliquid or not target then return true end
if mobkit.is_queue_empty_low(self) and self.isonground then
pos = mobkit.get_stand_pos(self)
local height = target.y - pos.y
water_life.dumbstep(self,height,target,0.1,5)
end
end
mobkit.queue_high(self,func,prty)
end
-- looks for a landing point on shore under air. tgt is optional
-- and must be an object, so it will start searching yaw2tgt - 15 degrees
function water_life.hq_go2land(self,prty,tgt)
local init = false
local offset = 1
local target = nil
local start = 1
if tgt then
local ayaw = water_life.get_yaw_to_object(self,tgt)
if ayaw then start = math.deg(ayaw) -15 end
end
local func = function(self)
local fpos = nil
local pos = mobkit.get_stand_pos(self)
if not init then
for i = start,359,15 do
local yaw = rad(i)
target = mobkit.pos_translate2d(pos,yaw,self.view_range)
fpos = water_life.find_collision(pos,target,false)
if fpos then
target = mobkit.pos_translate2d(pos,yaw,fpos+0.5)
local node=minetest.get_node({x=target.x,y=target.y+1,z=target.z})
if node.name == "air" then
--water_life.temp_show(target,5,2)
break
else
target = nil
end
else
target = nil
end
end
init = true
end
if self.isonground then return true end
if target then
local y=self.object:get_velocity().y
local pos2d = {x=pos.x,y=0,z=pos.z}
local dir=vector.normalize(vector.direction(pos2d,target))
local yaw = minetest.dir_to_yaw(dir)
if mobkit.timer(self,1) then
local pos1 = mobkit.pos_shift(mobkit.pos_shift(pos,{x=-dir.z*offset,z=dir.x*offset}),dir)
local h,l = mobkit.get_terrain_height(pos1)
if h and h>pos.y then
mobkit.lq_freejump(self)
else
local pos2 = mobkit.pos_shift(mobkit.pos_shift(pos,{x=dir.z*offset,z=-dir.x*offset}),dir)
local h,l = mobkit.get_terrain_height(pos2)
if h and h>pos.y then
mobkit.lq_freejump(self)
end
end
elseif mobkit.turn2yaw(self,yaw) then
dir.y = y
self.object:set_velocity(dir)
end
else
return true
end
--minetest.chat_send_all("angel= "..dump(yaw).." viewrange= "..dump(self.view_range).." distance= "..dump(vector.distance(pos,opos)))
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_snail_move(self,prty)
local ground = mobkit.get_stand_pos(self)
local coraltable = minetest.find_nodes_in_area({x=ground.x-3, y=ground.y-1, z=ground.z-3}, {x=ground.x+3, y=ground.y, z=ground.z+3}, water_life.urchinspawn)
if not coraltable or #coraltable < 1 then return end
local tgpos = coraltable[random(#coraltable)]
local func = function(self)
if not mobkit.is_alive(self) then return true end
local pos = mobkit.get_stand_pos(self)
local dist = vector.distance(pos,tgpos)
mobkit.drive_to_pos(self,tgpos,0.01,0.1,1.5)
--minetest.chat_send_all(dump(dist))
if dist <= 1.8 then return true end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_idle(self,prty,duration,anim)
anim = anim or 'stand'
local init = true
local func=function(self)
if init then
mobkit.animate(self,anim)
init=false
end
duration = duration-self.dtime
if duration <= 0 then return true end
end
mobkit.queue_high(self,func,prty)
end
-- swim to the next "node" which is inside viewrange or quit -- node can be string or table of string
-- if tgtpos is given node will be ignored
function water_life.hq_swimto(self,prty,speed,node,tgtpos)
local endpos = tgtpos
local pos = self.object:get_pos()
local r = self.view_range
if not tgtpos then
endpos = minetest.find_node_near(pos, r, node)
end
if not endpos then return true end
local func = function(self)
local yaw = water_life.get_yaw_to_pos(self,endpos)
if not mobkit.is_alive(self) then return true end
local pos = self.object:get_pos()
if mobkit.timer(self,1) then
if vector.distance(pos,endpos) > 1 then
--minetest.chat_send_all(vector.distance(pos,endpos))
if endpos.y > pos.y then
local vel = self.object:get_velocity()
vel.y = vel.y+0.4
self.object:set_velocity(vel)
end
if endpos.y < pos.y then
local vel = self.object:get_velocity()
vel.y = vel.y-0.1
self.object:set_velocity(vel)
end
mobkit.hq_aqua_turn(self,prty+5,yaw,speed)
pos = self.object:get_pos() --mobkit.get_stand_pos(self)
yaw = water_life.get_yaw_to_pos(self,endpos)
else
return true
end
end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_water_attack(self,tgtobj,prty,speed,shallow)
local pos = self.object:get_pos()
local selfbox = self.object:get_properties().collisionbox
local tgtbox = tgtobj:get_properties().collisionbox
if not speed then speed = 1 end
local func = function(self)
if not mobkit.is_alive(self) or not mobkit.is_alive(tgtobj) or tgtobj:get_attach() ~= nil then return true end
local pos = self.object:get_pos()
local endpos = tgtobj:get_pos()
if not shallow then
if not mobkit.is_in_deep(tgtobj) and vector.distance (pos,endpos) > 2 then return true end
else
if not water_life.isinliquid(tgtobj) and vector.distance (pos,endpos) > 2 then return true end
end
local yaw = water_life.get_yaw_to_pos(self,endpos)
local entity = nil
if not tgtobj:is_player() then entity = tgtobj:get_luaentity() end
if vector.distance(pos,endpos) > selfbox[5]+tgtbox[5] then
--minetest.chat_send_all(dump(vector.distance(pos,endpos)).." "..dump(selfbox[5]+tgtbox[5]))
if endpos.y > pos.y +tgtbox[5] then
local vel = self.object:get_velocity()
vel.y = vel.y+0.4
self.object:set_velocity(vel)
end
if endpos.y < pos.y then
local vel = self.object:get_velocity()
vel.y = vel.y-0.1
self.object:set_velocity(vel)
end
mobkit.hq_aqua_turn(self,prty+5,yaw,speed)
else
if mobkit.is_alive(tgtobj) then
--minetest.chat_send_all("<<<HIT>>>")
tgtobj:punch(self.object,1,self.attack)
return true
else
return true
end
end
if entity and string.match(entity.name,"petz") and vector.distance(pos,endpos) < 2 then
if mobkit.is_alive(tgtobj) then
--minetest.chat_send_all("<<<HIT>>>")
mobkit.hurt(entity,self.attack.damage_groups.fleshy or 4)
else
return true
end
end
end
mobkit.queue_high(self,func,prty)
end