a-planet-alive/my_changes/mobs/mobs_mobkit/water_life/files/behaviors.lua

1368 lines
38 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
local deg=math.deg
local tan = math.tan
local cos = math.cos
local atan=math.atan
-- flying stuff
local function chose_turn(self,a,b)
local remember = mobkit.recall(self,"turn")
if not remember then
if water_life.leftorright() then
remember = "1"
mobkit.remember(self,"time", self.time_total)
mobkit.remember(self,"turn", "1")
else
remember = "0"
mobkit.remember(self,"time", self.time_total)
mobkit.remember(self,"turn", "0")
end
end
if a > b then
mobkit.remember(self,"turn", "1")
mobkit.remember(self,"time", self.time_total)
return false
elseif a < b then
mobkit.remember(self,"turn","0")
mobkit.remember(self,"time", self.time_total)
return true
else
if remember == "0" then return true else return false end
end
end
local function pitchroll2pitchyaw(aoa,roll)
if roll == 0.0 then return aoa,0 end
-- assumed vector x=0,y=0,z=1
local p1 = tan(aoa)
local y = cos(roll)*p1
local x = sqrt(p1^2-y^2)
local pitch = atan(y)
local yaw=atan(x)*math.sign(roll)
return pitch,yaw
end
------------------
-- LQ behaviors --
------------------
--[[
params:
lift: [number]
multiplier for lift. faster objects need less, slower need more. typical value: 0.6 for speeds around 4 m/s
pitch: [degrees]
angle between the longitudinal axis and horizontal plane. typical range: <-15.15>
aoa:
[degrees] angle of attack - the angle between the longitudinal axis and velocity vector.
roll: [degrees]
bank angle. positive is right, negative is left, this is how they turn. if set too large they'll loose height rapidly
acc: [number]
propulsion. use with positive pitch to make them fly level or climb, set it to 0 with slight negative pitch to make
them glide. typical value: around 1.0
anim: [string]
animation.
The example uses two simple high level behaviors to keep them between 18 and 24 nodes above ground, seems good already for ambient type flying creatures.
warning: never set_velocity when using these behaviors.
]]
function water_life.lq_fly_aoa(self,lift,aoa,roll,acc,anim)
aoa=rad(aoa)
roll=rad(roll)
local hpitch = 0
local hyaw = 0
local caoa = 0
local laoa = nil
local croll=roll
local lroll = nil
local lastrot = nil
local init = true
local func=function(self)
local rotation=self.object:get_rotation()
local vel = self.object:get_velocity()
local vrot = mobkit.dir_to_rot(vel,lastrot)
lastrot = vrot
if init then
if anim then mobkit.animate(self,anim) end
init = false
end
local accel=self.object:get_acceleration()
-- gradual changes
if abs(roll-rotation.z) > 0.5*self.dtime then
croll = rotation.z+0.5*self.dtime*math.sign(roll-rotation.z)
end
if croll~=lroll then
hpitch,hyaw = pitchroll2pitchyaw(aoa,croll)
lroll = croll
end
local hrot = {x=vrot.x+hpitch,y=vrot.y-hyaw,z=croll}
self.object:set_rotation(hrot)
local hdir = mobkit.rot_to_dir(hrot)
local cross = vector.cross(vel,hdir)
local lift_dir = vector.normalize(vector.cross(cross,hdir))
local daoa = deg(aoa)
local lift_coefficient = 0.24*abs(daoa)*(1/(0.025*daoa+1))^4*math.sign(aoa) -- homegrown formula
local lift_val = lift*vector.length(vel)^2*lift_coefficient
local lift_acc = vector.multiply(lift_dir,lift_val)
lift_acc=vector.add(vector.multiply(minetest.yaw_to_dir(rotation.y),acc),lift_acc)
self.object:set_acceleration(vector.add(accel,lift_acc))
end
mobkit.queue_low(self,func)
end
function water_life.lq_fly_pitch(self,lift,pitch,roll,acc,anim)
pitch = rad(pitch)
roll=rad(roll)
local cpitch = pitch
local croll = roll
local hpitch = 0
local hyaw = 0
local lpitch = nil
local lroll = nil
local lastrot = nil
local init = true
local func=function(self)
if init then
if anim then mobkit.animate(self,anim) end
init = false
end
local rotation=self.object:get_rotation()
local accel=self.object:get_acceleration()
local vel = self.object:get_velocity()
local speed = vector.length(vel)
local vdir = vector.normalize(vel)
local vrot = mobkit.dir_to_rot(vel,lastrot)
lastrot = vrot
-- gradual changes
if abs(roll-rotation.z) > 0.5*self.dtime then
croll = rotation.z+0.5*self.dtime*math.sign(roll-rotation.z)
end
if abs(pitch-rotation.x) > 0.5*self.dtime then
cpitch = rotation.x+0.5*self.dtime*math.sign(pitch-rotation.x)
end
if cpitch~=lpitch or croll~=lroll then
hpitch,hyaw = pitchroll2pitchyaw(cpitch,croll)
lpitch = cpitch lroll = croll
end
local aoa = deg(-vrot.x+cpitch) -- angle of attack
local hrot = {x=hpitch, y=vrot.y-hyaw, z=croll} -- hull rotation
self.object:set_rotation(hrot)
local hdir = mobkit.rot_to_dir(hrot) -- hull dir
local cross = vector.cross(hdir,vel)
local lift_dir = vector.normalize(vector.cross(hdir,cross))
local lift_coefficient = 0.24*max(aoa,0)*(1/(0.025*max(aoa,0)+1))^4 -- homegrown formula
-- local lift_val = mobkit.minmax(lift*speed^2*lift_coefficient,speed/self.dtime)
-- local lift_val = max(lift*speed^2*lift_coefficient,0)
local lift_val = min(lift*speed^2*lift_coefficient,20)
--if lift_val > 10 then minetest.chat_send_all('lift: '.. lift_val ..' vel:'.. speed ..' aoa:'.. aoa) end
local lift_acc = vector.multiply(lift_dir,lift_val)
lift_acc=vector.add(vector.multiply(minetest.yaw_to_dir(rotation.y),acc),lift_acc)
accel=vector.add(accel,lift_acc)
accel=vector.add(accel,vector.multiply(vdir,-speed*speed*0.02)) -- drag
accel=vector.add(accel,vector.multiply(hdir,acc)) -- propeller
self.object:set_acceleration(accel)
end
mobkit.queue_low(self,func)
end
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
function water_life.lq_jumpattack(self,height,target,extra)
local phase=1
local timer=0.5
local tgtbox = target:get_properties().collisionbox
local func=function(self)
local selfname = self.object:get_luaentity().name
if not mobkit.is_alive(target) then return true end
if self.isonground then
if phase==1 then -- collision bug workaround
local vel = self.object:get_velocity()
vel.y = -mobkit.gravity*sqrt(height*2/-mobkit.gravity)
self.object:set_velocity(vel)
mobkit.make_sound(self,'charge')
phase=2
else
mobkit.lq_idle(self,0.3)
return true
end
elseif phase==2 then
local dir = minetest.yaw_to_dir(self.object:get_yaw())
local vy = self.object:get_velocity().y
dir=vector.multiply(dir,6)
dir.y=vy
self.object:set_velocity(dir)
phase=3
elseif phase==3 then -- in air
local tgtpos = target:get_pos()
local pos = self.object:get_pos()
-- calculate attack spot
local yaw = self.object:get_yaw()
local dir = minetest.yaw_to_dir(yaw)
local apos = mobkit.pos_translate2d(pos,yaw,self.attack.range)
if mobkit.is_pos_in_box(apos,tgtpos,tgtbox) then --bite
target:punch(self.object,1,self.attack)
if selfname and target:is_player() then
if selfname == "water_life:snake" then
local meta = target:get_meta()
local name = target:get_player_name()
local join = meta:get_int("jointime")
if not join or (os.time() - join) > water_life.newplayerbonus * 86400 then
hunger_ng.set_effect(target:get_player_name(), "heal", "disabled")
meta:set_int("snakepoison",1)
water_life.change_hud(target,"poison")
else
local left = water_life.newplayerbonus - math.floor((os.time() - join)/86400*100)/100
minetest.chat_send_player(target:get_player_name(),minetest.colorize('#fd4000',">>> A rattlesnake bit you. New player bonus of "..left..
" days left. Catch 3 snakes to craft antiserum"))
meta:set_int("bitten",1)
minetest.after(10,function()
meta:set_int("bitten",0)
end,meta)
end
end
end
-- bounce off
local vy = self.object:get_velocity().y
self.object:set_velocity({x=dir.x*-3,y=vy,z=dir.z*-3})
-- play attack sound if defined
mobkit.make_sound(self,'attack')
phase=4
end
end
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
function water_life.hq_aquaidle(self,prty,anim)
local init = true
if not anim then anim = 'def' end
local func = function(self)
if init then
mobkit.animate(self,anim)
self.object:set_velocity({x=0,y=0,z=0})
init = false
end
if self.name == "water_life:alligator" then
if random(100) < 5 then
mobkit.animate(self,'roll')
end
end
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,anim)
local tyaw = 0
local init = true
local prvscanpos = {x=0,y=0,z=0}
local center = self.object:get_pos()
if not anim then anim = 'def' end
local func = function(self)
if init then
mobkit.animate(self,anim)
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,anim)
if not anim then anim = "def" end
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,anim)
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 meta = nil
local poison = 0
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 tgtobj:is_player() then
meta = tgtobj:get_meta()
poison = meta:get_int("snakepoison")
noob = meta:get_int("bitten")
end
if dist > 3 or poison > 0 or noob > 0 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")
water_life.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,anim)
if not lost then lost = self.view_range end
if random(100) < 20 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)
local meta = nil
local poison = 0
local noob = 0
if tgtobj:is_player() then
meta = tgtobj:get_meta()
poison = meta:get_int("snakepoison")
noob = meta:get_int("bitten")
end
if poison > 0 or noob > 0 then return true end
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,idle)
if not idle then idle = random(30,120) end
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,idle) 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,speed)
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
if not speed then speed = 0.1 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,speed,0)
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
-- turn around 180degrees from tgtob and swim away until out of sight
function water_life.hq_swimfrom(self,prty,tgtobj,speed,outofsight)
local init = true
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 self.isonground then return true end
if init then
mobkit.animate(self,"swim")
init=false
end
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
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+selfbox[5] then
local vel = vector.add(self.object:get_velocity(),{x=0,y=0.5,z=0})
self.object:set_velocity(vel)
end
if endpos.y < pos.y-selfbox[5] then
local vel = vector.add(self.object:get_velocity(),{x=0,y=-0.5,z=0})
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
-- hq flying behaviors
function water_life.hq_climb(self,prty,fmin,fmax)
if not max then max = 30 end
if not min then min = 20 end
local func=function(self)
if mobkit.timer(self,1) then
local remember = mobkit.recall(self,"time")
if remember then
if self.time_total - remember > 15 then
mobkit.forget(self,"turn")
mobkit.forget(self,"time")
end
end
self.action = "fly"
local pos = self.object:get_pos()
local yaw = self.object:get_yaw()
--local tim = minetest.get_us_time()
local left, right, up, down, under, above = water_life.radar(pos,yaw,32,true)
--minetest.chat_send_all(minetest.get_us_time()-tim)
if (down < 3) and (under >= fmax) then
water_life.hq_glide(self,prty,fmin,fmax)
return true
end
if left > 3 or right > 3 then
local lift = 0.6
local pitch = 8
local roll = 6
local acc = 1.2
--roll = (max(left,right)/30 *3)+(down/100)*3+roll
roll = (max(left,right)/30 * 7.5)
lift = lift + (down - up) /400
pitch = pitch + (down - up) /30
--lift = lift + (down/100) - (up/100)
local turn = chose_turn(self,left,right)
if turn then
mobkit.clear_queue_low(self)
water_life.lq_fly_pitch(self,lift,pitch,roll*-1,acc,'fly')
else
mobkit.clear_queue_low(self)
water_life.lq_fly_pitch(self,lift,pitch,roll,acc,'fly')
end
end
end
if mobkit.timer(self,15) then mobkit.clear_queue_low(self) end
if mobkit.is_queue_empty_low(self) then water_life.lq_fly_pitch(self,0.6,8,(random(2)-1.5)*30,1.2,'fly') end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_glide(self,prty,fmin,fmax)
if not max then fmax = 30 end
if not min then fmin = 20 end
local func = function(self)
if mobkit.timer(self,1) then
self.action = "glide"
local remember = mobkit.recall(self,"time")
if remember then
if self.time_total - remember > 15 then
mobkit.forget(self,"turn")
mobkit.forget(self,"time")
end
end
local pos = self.object:get_pos()
local yaw = self.object:get_yaw()
local left, right, up, down, under, above = water_life.radar(pos,yaw,32,true)
if (down > 10) or (under < fmin) then
water_life.hq_climb(self,prty,fmin,fmax)
return true
end
if left > 3 or right > 3 then
local lift = 0.6
local pitch = 8
local roll = 0
local acc = 1.2
--roll = (max(left,right)/30 *3)+(down/100)*3+roll
roll = (max(left,right)/30 *7.5)
local turn = chose_turn(self,left,right)
if turn then
mobkit.clear_queue_low(self)
water_life.lq_fly_pitch(self,lift,pitch,roll*-1,acc,'glide')
else
mobkit.clear_queue_low(self)
water_life.lq_fly_pitch(self,lift,pitch,roll,acc,'glide')
end
end
end
if mobkit.timer(self,20) then mobkit.clear_queue_low(self) end
if mobkit.is_queue_empty_low(self) then water_life.lq_fly_pitch(self,0.6,-4,(random(2)-1.5)*30,0,'glide') end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_water_takeoff(self,prty,anim,tyaw)
local init = true
local startup = true
local turned = false
local timer = 0
if not anim then anim = 'def' end
local pos2 = {}
local func=function(self)
local yaw = self.object:get_yaw()
local pos = self.object:get_pos()
if startup then
if tyaw then yaw = tyaw end
--water_life.temp_show(pos,5,1)
for i = 0,330,30 do
pos2 = mobkit.pos_translate2d(pos,yaw+rad(i),self.view_range*2)
if not water_life.find_collision(pos,pos2,false) then
tyaw = yaw + rad(i)
--water_life.temp_show(pos2,5,10)
break
end
end
startup = false
end
if not startup then
if not tyaw then return true end
if mobkit.turn2yaw(self,tyaw,5) then turned = true end
end
if turned then
if init then
self.object:set_velocity({x=0, y=0.5, z=0})
minetest.after(2,function()
mobkit.animate(self,anim)
end)
init = false
end
minetest.after(4,function()
mobkit.animate(self,'fly')
end)
if timer > 3 then
self.object:add_velocity({x=0,y=0.25,z=0})
end
mobkit.go_forward_horizontal(self,3)
timer = timer + self.dtime
if timer > 8 then
local vec = vector.multiply(minetest.yaw_to_dir(tyaw),2)
vec.y = vec.y + 4
self.object:add_velocity(vec)
mobkit.remember(self,"airlife",os.time())
mobkit.forget(self,"landlife")
mobkit.forget(self,"waterlife")
return true
end
end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_fly2obj(self,prty,tgt,break_dist,force)
local func=function(self)
if not break_dist then break_dist = 5 end
if not tgt then
mobkit.clear_queue_high(self)
water_life.hq_climb(self,prty)
return true
end
local wname = ""
local roll = 0
local pos = self.object:get_pos()
local yaw = self.object:get_yaw()
local tgtpos = tgt:get_pos()
local tgtyaw = tgt:get_yaw() --water_life.get_yaw_to_object(self,tgt)
local tgtspeed = math.floor(vector.length(tgt:get_velocity() or {x=0,y=0,z=0}))
if not tgt:is_player() and tgt:get_luaentity() and tgt:get_luaentity().name == "water_life:whale" then tgtyaw = tgtyaw + rad(180) end -- whales moving backwards XD
if tgt:is_player() then
tgtyaw = tgt:get_look_horizontal()
local stack = tgt:get_wielded_item()
wname = stack:get_name()
end
if tgtpos.y < 0 then tgtpos.y = 1 end
if not water_life.gull_bait[wname] and not force then
mobkit.clear_queue_high(self)
mobkit.clear_queue_low(self)
water_life.hq_climb(self,15,4,16)
return true
end
--minetest.chat_send_all(dump(tgtpos).." "..dump(tgtyaw).." "..dump(tgtspeed))
if not tgtyaw or not tgtspeed or not mobkit.is_alive(tgt) or self.isonground or self.isinliquid then
mobkit.clear_queue_high(self)
water_life.hq_climb(self,prty)
return true
end
local turn = 0
local diff = 0
local lift = 1.2
local pitch = 5
local acc = 0.6
local anim = "fly"
local truetpos=mobkit.pos_translate2d(tgtpos,tgtyaw,tgtspeed*3)
local ddistance = vector.distance(pos,{x=truetpos.x,y= pos.y, z=truetpos.z})
local alpha = atan((pos.y - truetpos.y)/ ddistance)
local truetyaw = water_life.get_yaw_to_pos(self,truetpos)
local realdistance = vector.distance(pos,tgtpos)
local ang2tgt = mobkit.pos_translate2d(pos,truetyaw,15)
--chose right or left turn
if yaw < truetyaw then
turn = -1
elseif yaw > truetyaw then
turn = 1
end
diff = abs(truetyaw - yaw)
-- or chose straight ahead
if ddistance > 30 and diff <= 0.5 then
turn = 0
end
if ddistance > 20 and diff <= 0.3 then
turn = 0
end
if diff <= 0.1 then
turn = 0
end
--minetest.chat_send_all(">>> "..dump(math.floor(diff*100)/100).." <<<")
--minetest.chat_send_all("distance ="..dump(math.floor(ddistance*100)/100).." yawdiff ="..dump(math.floor((truetyaw-yaw)*100)/100))
if ddistance > 32 then
roll = 15 * turn
elseif ddistance > 22 then
roll = 10 * turn
elseif ddistance > 12 then
roll = 5 * turn
elseif ddistance <= 12 then
roll = 2 * turn
end
--water_life.temp_show(truetpos,1,3)
--minetest.chat_send_all(dump(minetest.pos_to_string(truetpos,2)).." -- "..dump(minetest.pos_to_string(pos,2)))
if pos.y > truetpos.y + 1 and pos.y > 2 and ddistance < 25 then
anim = "glide"
pitch = -10
elseif pos.y < truetpos.y - 1 then
pitch = 15
else
pitch = 5
end
--tim = minetest.get_us_time()
local left,right,center,up,down = water_life.radar_fast(self,20)
--minetest.chat_send_all(minetest.get_us_time()-tim)
if down and down < 16 then pitch = 15 end
if up and not down and not center then pitch = -10 end
if right and not center then roll = 10 end
if left and not center then roll = -10 end
if left and right and not center then roll = 0 end
if center and down and center < 5 then
mobkit.clear_queue_high(self)
mobkit.clear_queue_low(self)
water_life.hq_climb(self,15,4,16)
return true
end
if water_life.radar_debug then
water_life.temp_show(ang2tgt,1)
for i = 1,10,1 do
water_life.temp_show({x=truetpos.x, y=truetpos.y+i*2, z=truetpos.z},1)
end
--minetest.chat_send_all("Alpha= "..dump(alpha)..", Hight= "..dump(math.floor(pos.y)).." ###"..dump(yaw).."### hityaw="..dump(truetyaw))
--minetest.chat_send_all("distance ="..dump(math.floor(ddistance*100)/100).." Alpha ="..dump(math.floor(deg(alpha)*100)/100))
--minetest.chat_send_all("distance2prey ="..dump(vector.distance(pos,tgtpos)))
end
mobkit.clear_queue_low(self)
if realdistance < break_dist+0.5 then -- steal bait from players
-- minetest.chat_send_all(">>> "..dump(math.floor(ddistance*100)/100).."<<< ###"..dump(math.floor(realdistance*100)/100).." ###")
if tgt:is_player() then
local bstack = tgt:get_wielded_item()
local bait = bstack:get_name()
if water_life.gull_bait[bait] then
bstack:take_item(1)
tgt:set_wielded_item(bstack)
end
end
mobkit.clear_queue_high(self)
mobkit.clear_queue_low(self)
water_life.hq_climb(self,15,4,16)
return true
else
water_life.lq_fly_pitch(self,lift,pitch,roll,acc,anim)
end
end
mobkit.queue_high(self,func,prty)
end
--snakes
function water_life.hq_snake_warn(self,target,prty,duration,anim)
anim = anim or 'warn'
local init = true
local func=function(self)
if init then
mobkit.make_sound(self,"warn")
minetest.after(1,function(anim)
mobkit.animate(self,anim)
end,anim)
init=false
end
local yaw = water_life.get_yaw_to_object(self,target)
self.object:set_yaw(yaw)
duration = duration-self.dtime
local dist = water_life.dist2tgt(self,target)
if dist > self.view_range then
minetest.after(3,function()
return true
end)
end
if duration <= 0 or dist < 4 then
mobkit.remember(self,"warned",target:get_player_name())
return true
end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_snake_move(self,prty,anim)
anim = anim or 'look'
local init = true
local getpos = nil
local func=function(self)
local getpos = nil
local pos = mobkit.get_stand_pos(self) --self.object:get_pos()
local yaw = 0
if init then
mobkit.animate(self,anim)
init=false
yaw = rad(random(360))
pos = mobkit.pos_translate2d(pos,yaw,self.view_range+5)
getpos = water_life.find_node_under_air(pos,self.view_range)
--water_life.temp_show(getpos,5,5)
end
if getpos then
water_life.hq_idle(self,prty+2,5,anim)
water_life.hq_findpath(self,prty+1,getpos, 1.5,0.1,true)
return true
else
return true
end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_snakerun(self,prty,tgtobj)
local init=true
local timer=6
local func = function(self)
if not mobkit.is_alive(tgtobj) then return true end
if self.isinliquid then return true end
if init then
timer = timer-self.dtime
if timer <=0 or vector.distance(self.object:get_pos(),tgtobj:get_pos()) < 8 then
mobkit.make_sound(self,'scared')
init=false
end
return
end
if mobkit.is_queue_empty_low(self) and self.isonground then
local pos = mobkit.get_stand_pos(self)
local opos = tgtobj:get_pos()
if vector.distance(pos,opos) < (self.view_range*3) then
--minetest.chat_send_all(dump(vector.distance(pos,opos)).." "..dump(self.view_range*3))
local tpos = {x=2*pos.x - opos.x,
y=opos.y,
z=2*pos.z - opos.z}
water_life.goto_next_waypoint(self,tpos)
else
mobkit.clear_queue_low(self)
mobkit.clear_queue_high(self)
--water_life.hq_idle(self,10,random(60,120),"sleep")
water_life.hq_snake_move(self,15)
return true
end
end
end
mobkit.queue_high(self,func,prty)
end
function water_life.hq_runfrom(self,prty,tgtobj)
local init=true
local timer=6
local func = function(self)
if self.isinliquid then return true end
if not mobkit.is_alive(tgtobj) then return true end
--[[if init then
timer = timer-self.dtime
if timer <=0 or vector.distance(self.object:get_pos(),tgtobj:get_pos()) < 8 then
mobkit.make_sound(self,'scared')
init=false
end
return
end]]
if mobkit.is_queue_empty_low(self) and self.isonground then
local pos = mobkit.get_stand_pos(self)
local opos = tgtobj:get_pos()
if vector.distance(pos,opos) < self.view_range*1.1 then
local tpos = {x=2*pos.x - opos.x,
y=opos.y,
z=2*pos.z - opos.z}
mobkit.goto_next_waypoint(self,tpos)
else
self.object:set_velocity({x=0,y=0,z=0})
return true
end
end
end
mobkit.queue_high(self,func,prty)
end
-- dying
function water_life.hq_die(self,anim)
local timer = 5
local start = true
local func = function(self)
if start then
if not anim then
mobkit.lq_fallover(self)
else
mobkit.animate(self,anim)
end
self.logic = function(self) end -- brain dead as well
start=false
end
timer = timer-self.dtime
if timer < 0 then self.object:remove() end
end
mobkit.queue_high(self,func,100)
end