Add files via upload
parent
d7288f6c2b
commit
04ae1483e7
|
@ -0,0 +1,570 @@
|
||||||
|
-- node by node land movement macros
|
||||||
|
function mobkit.get_next_waypoint(self,tpos)
|
||||||
|
local pos = mobkit.get_stand_pos(self)
|
||||||
|
local dir=vector.direction(pos,tpos)
|
||||||
|
local neighbor = mobkit.dir2neighbor(dir)
|
||||||
|
local function update_pos_history(self,pos)
|
||||||
|
table.insert(self.pos_history,1,pos)
|
||||||
|
if #self.pos_history > 2 then table.remove(self.pos_history,#self.pos_history) end
|
||||||
|
end
|
||||||
|
local nogopos = self.pos_history[2]
|
||||||
|
|
||||||
|
local height, pos2, liquidflag = mobkit.is_neighbor_node_reachable(self,neighbor)
|
||||||
|
--minetest.chat_send_all('pos2 ' .. minetest.serialize(pos2))
|
||||||
|
--minetest.chat_send_all('nogopos ' .. minetest.serialize(nogopos))
|
||||||
|
if height and not liquidflag
|
||||||
|
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||||
|
|
||||||
|
local heightl = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-1))
|
||||||
|
if heightl and abs(heightl-height)<0.001 then
|
||||||
|
local heightr = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,1))
|
||||||
|
if heightr and abs(heightr-height)<0.001 then
|
||||||
|
dir.y = 0
|
||||||
|
local dirn = vector.normalize(dir)
|
||||||
|
local npos = mobkit.get_node_pos(mobkit.pos_shift(pos,neighbors[neighbor]))
|
||||||
|
local factor = abs(dirn.x) > abs(dirn.z) and abs(npos.x-pos.x) or abs(npos.z-pos.z)
|
||||||
|
pos2=mobkit.pos_shift(pos,{x=dirn.x*factor,z=dirn.z*factor})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
update_pos_history(self,pos2)
|
||||||
|
return height, pos2
|
||||||
|
else
|
||||||
|
|
||||||
|
for i=1,3 do
|
||||||
|
-- scan left
|
||||||
|
local height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-i*self.path_dir))
|
||||||
|
if height and not liq
|
||||||
|
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||||
|
update_pos_history(self,pos2)
|
||||||
|
return height,pos2
|
||||||
|
end
|
||||||
|
-- scan right
|
||||||
|
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,i*self.path_dir))
|
||||||
|
if height and not liq
|
||||||
|
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||||
|
update_pos_history(self,pos2)
|
||||||
|
return height,pos2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--scan rear
|
||||||
|
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,4))
|
||||||
|
if height and not liq
|
||||||
|
and not (nogopos and mobkit.isnear2d(pos2,nogopos,0.1)) then
|
||||||
|
update_pos_history(self,pos2)
|
||||||
|
return height,pos2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- stuck condition here
|
||||||
|
table.remove(self.pos_history,2)
|
||||||
|
self.path_dir = self.path_dir*-1 -- subtle change in pathfinding
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.get_next_waypoint_fast(self,tpos,nogopos)
|
||||||
|
local pos = mobkit.get_stand_pos(self)
|
||||||
|
local dir=vector.direction(pos,tpos)
|
||||||
|
local neighbor = mobkit.dir2neighbor(dir)
|
||||||
|
local height, pos2, liquidflag = mobkit.is_neighbor_node_reachable(self,neighbor)
|
||||||
|
|
||||||
|
if height and not liquidflag then
|
||||||
|
local fast = false
|
||||||
|
heightl = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-1))
|
||||||
|
if heightl and abs(heightl-height)<0.001 then
|
||||||
|
heightr = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,1))
|
||||||
|
if heightr and abs(heightr-height)<0.001 then
|
||||||
|
fast = true
|
||||||
|
dir.y = 0
|
||||||
|
local dirn = vector.normalize(dir)
|
||||||
|
local npos = mobkit.get_node_pos(mobkit.pos_shift(pos,neighbors[neighbor]))
|
||||||
|
local factor = abs(dirn.x) > abs(dirn.z) and abs(npos.x-pos.x) or abs(npos.z-pos.z)
|
||||||
|
pos2=mobkit.pos_shift(pos,{x=dirn.x*factor,z=dirn.z*factor})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return height, pos2, fast
|
||||||
|
else
|
||||||
|
|
||||||
|
for i=1,4 do
|
||||||
|
-- scan left
|
||||||
|
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,-i))
|
||||||
|
if height and not liq then return height,pos2 end
|
||||||
|
-- scan right
|
||||||
|
height, pos2, liq = mobkit.is_neighbor_node_reachable(self,mobkit.neighbor_shift(neighbor,i))
|
||||||
|
if height and not liq then return height,pos2 end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.goto_next_waypoint(self,tpos)
|
||||||
|
local height, pos2 = mobkit.get_next_waypoint(self,tpos)
|
||||||
|
|
||||||
|
if not height then return false end
|
||||||
|
|
||||||
|
if height <= 0.01 then
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
local tyaw = minetest.dir_to_yaw(vector.direction(self.object:get_pos(),pos2))
|
||||||
|
if abs(tyaw-yaw) > 1 then
|
||||||
|
mobkit.lq_turn2pos(self,pos2)
|
||||||
|
end
|
||||||
|
mobkit.lq_dumbwalk(self,pos2)
|
||||||
|
else
|
||||||
|
mobkit.lq_turn2pos(self,pos2)
|
||||||
|
mobkit.lq_dumbjump(self,height)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.dumbstep(self,height,tpos,speed_factor)
|
||||||
|
if height <= 0.001 then
|
||||||
|
mobkit.lq_turn2pos(self,tpos)
|
||||||
|
mobkit.lq_dumbwalk(self,tpos,speed_factor)
|
||||||
|
else
|
||||||
|
mobkit.lq_turn2pos(self,tpos)
|
||||||
|
mobkit.lq_dumbjump(self,height)
|
||||||
|
end
|
||||||
|
mobkit.lq_idle(self,random(1,6))
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
-- LOW LEVEL QUEUE FUNCTIONS
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
function mobkit.lq_turn2pos(self,tpos)
|
||||||
|
local func=function(self)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
return mobkit.turn2yaw(self,
|
||||||
|
minetest.dir_to_yaw(vector.direction(pos,tpos)))
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.lq_idle(self,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_low(self,func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.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
|
||||||
|
|
||||||
|
if mobkit.is_there_yet2d(pos,minetest.yaw_to_dir(self.object:get_yaw()),dest) then
|
||||||
|
-- if mobkit.isnear2d(pos,dest,0.25) then
|
||||||
|
if not self.isonground or abs(dest.y-pos.y) > 0.1 then -- prevent uncontrolled fall when velocity too high
|
||||||
|
-- if abs(dest.y-pos.y) > 0.1 then -- isonground too slow for speeds > 4
|
||||||
|
self.object:set_velocity({x=0,y=y,z=0})
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.isonground then
|
||||||
|
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)
|
||||||
|
-- self.object:set_yaw(minetest.dir_to_yaw(dir))
|
||||||
|
mobkit.turn2yaw(self,minetest.dir_to_yaw(dir))
|
||||||
|
dir.y = y
|
||||||
|
self.object:set_velocity(dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initial velocity for jump height h, v= a*sqrt(h*2/a) ,add 20%
|
||||||
|
function mobkit.lq_dumbjump(self,height,anim)
|
||||||
|
anim = anim or 'stand'
|
||||||
|
local jump = true
|
||||||
|
local func=function(self)
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
if self.isonground then
|
||||||
|
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
|
||||||
|
else
|
||||||
|
local dir = minetest.yaw_to_dir(yaw)
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
if self.lastvelocity.y < 0.9 then
|
||||||
|
dir = vector.multiply(dir,3)
|
||||||
|
end
|
||||||
|
dir.y = vel.y
|
||||||
|
self.object:set_velocity(dir,yaw)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.lq_jumpout(self)
|
||||||
|
local phase = 1
|
||||||
|
local func=function(self)
|
||||||
|
local vel=self.object:get_velocity()
|
||||||
|
if phase == 1 then
|
||||||
|
vel.y=vel.y+5
|
||||||
|
self.object:set_velocity(vel)
|
||||||
|
phase = 2
|
||||||
|
else
|
||||||
|
if vel.y < 0 then return true end
|
||||||
|
local dir = minetest.yaw_to_dir(self.object:get_yaw())
|
||||||
|
dir.y=vel.y
|
||||||
|
self.object:set_velocity(dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.lq_freejump(self)
|
||||||
|
local phase = 1
|
||||||
|
local func=function(self)
|
||||||
|
local vel=self.object:get_velocity()
|
||||||
|
if phase == 1 then
|
||||||
|
vel.y=vel.y+6
|
||||||
|
self.object:set_velocity(vel)
|
||||||
|
phase = 2
|
||||||
|
else
|
||||||
|
if vel.y <= 0.01 then return true end
|
||||||
|
local dir = minetest.yaw_to_dir(self.object:get_yaw())
|
||||||
|
dir.y=vel.y
|
||||||
|
self.object:set_velocity(dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.lq_jumpattack(self,height,target)
|
||||||
|
local phase=1
|
||||||
|
local func=function(self)
|
||||||
|
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
|
||||||
|
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 twidth = target:get_properties().collisionbox[1]
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
-- calculate attack spot
|
||||||
|
local dir = minetest.yaw_to_dir(self.object:get_yaw())
|
||||||
|
dir2 = vector.add(dir,self.attack.range+twidth)
|
||||||
|
local apos = vector.add(pos,dir2)
|
||||||
|
-- local tpos = mobkit.get_stand_pos(target) --test
|
||||||
|
-- tpos.y = tpos.y+height
|
||||||
|
if mobkit.isnear2d(apos,target:get_pos(),0.25) then --bite
|
||||||
|
target:punch(self.object,1,self.attack)
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
function mobkit.lq_fallover(self)
|
||||||
|
local zrot = 0
|
||||||
|
local init = true
|
||||||
|
local func=function(self)
|
||||||
|
if init then
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
self.object:set_velocity(mobkit.pos_shift(vel,{y=1}))
|
||||||
|
mobkit.animate(self,'stand')
|
||||||
|
init = false
|
||||||
|
end
|
||||||
|
zrot=zrot+pi*0.05
|
||||||
|
local rot = self.object:get_rotation()
|
||||||
|
self.object:set_rotation({x=rot.x,y=rot.y,z=zrot})
|
||||||
|
if zrot >= pi*0.5 then return true end
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func)
|
||||||
|
end
|
||||||
|
-----------------------------
|
||||||
|
-- HIGH LEVEL QUEUE FUNCTIONS
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
function mobkit.hq_roam(self,prty)
|
||||||
|
local func=function(self)
|
||||||
|
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.3) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_follow0(self,tgtobj) -- probably delete this one
|
||||||
|
local func = function(self)
|
||||||
|
if not tgtobj 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()
|
||||||
|
if vector.distance(pos,opos) > 3 then
|
||||||
|
local neighbor = mobkit.dir2neighbor(vector.direction(pos,opos))
|
||||||
|
if not neighbor then return true end --temp debug
|
||||||
|
local height, tpos = mobkit.is_neighbor_node_reachable(self,neighbor)
|
||||||
|
if height then mobkit.dumbstep(self,height,tpos)
|
||||||
|
else
|
||||||
|
for i=1,4 do --scan left
|
||||||
|
height, tpos = mobkit.is_neighbor_node_reachable(self,(8+neighbor-i-1)%8+1)
|
||||||
|
if height then mobkit.dumbstep(self,height,tpos)
|
||||||
|
break
|
||||||
|
end --scan right
|
||||||
|
height, tpos = mobkit.is_neighbor_node_reachable(self,(neighbor+i-1)%8+1)
|
||||||
|
if height then mobkit.dumbstep(self,height,tpos)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mobkit.lq_idle(self,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_follow(self,prty,tgtobj)
|
||||||
|
local func = function(self)
|
||||||
|
if not mobkit.is_alive(tgtobj) 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()
|
||||||
|
if vector.distance(pos,opos) > 3 then
|
||||||
|
mobkit.goto_next_waypoint(self,opos)
|
||||||
|
else
|
||||||
|
mobkit.lq_idle(self,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_goto(self,prty,tpos)
|
||||||
|
local func = function(self)
|
||||||
|
if mobkit.is_queue_empty_low(self) and self.isonground then
|
||||||
|
local pos = mobkit.get_stand_pos(self)
|
||||||
|
if vector.distance(pos,tpos) > 3 then
|
||||||
|
mobkit.goto_next_waypoint(self,tpos)
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_runfrom(self,prty,tgtobj)
|
||||||
|
local init=true
|
||||||
|
local timer=6
|
||||||
|
local func = function(self)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
function mobkit.hq_hunt(self,prty,tgtobj)
|
||||||
|
local func = function(self)
|
||||||
|
if not mobkit.is_alive(tgtobj) 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 dist > self.view_range then
|
||||||
|
return true
|
||||||
|
elseif dist > 3 then
|
||||||
|
mobkit.goto_next_waypoint(self,opos)
|
||||||
|
else
|
||||||
|
mobkit.hq_attack(self,prty+1,tgtobj)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_warn(self,prty,tgtobj)
|
||||||
|
timer=0
|
||||||
|
tgttime = 0
|
||||||
|
local func = function(self)
|
||||||
|
if not mobkit.is_alive(tgtobj) then return true end
|
||||||
|
local pos = mobkit.get_stand_pos(self)
|
||||||
|
local opos = tgtobj:get_pos()
|
||||||
|
local dist = vector.distance(pos,opos)
|
||||||
|
|
||||||
|
if dist > 11 then
|
||||||
|
return true
|
||||||
|
elseif dist < 4 or timer > 12 then -- too close man
|
||||||
|
-- mobkit.clear_queue_high(self)
|
||||||
|
mobkit.remember(self,'hate',tgtobj:get_player_name())
|
||||||
|
mobkit.hq_hunt(self,prty+1,tgtobj) -- priority
|
||||||
|
else
|
||||||
|
timer = timer+self.dtime
|
||||||
|
if mobkit.is_queue_empty_low(self) then
|
||||||
|
mobkit.lq_turn2pos(self,opos)
|
||||||
|
end
|
||||||
|
-- make noise in random intervals
|
||||||
|
if timer > tgttime then
|
||||||
|
mobkit.make_sound(self,'warn')
|
||||||
|
-- if self.sounds and self.sounds.warn then
|
||||||
|
-- minetest.sound_play(self.sounds.warn, {object=self.object})
|
||||||
|
-- end
|
||||||
|
tgttime = timer + 1.1 + random()*1.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_die(self)
|
||||||
|
local timer = 5
|
||||||
|
local start = true
|
||||||
|
local func = function(self)
|
||||||
|
if start then
|
||||||
|
mobkit.lq_fallover(self)
|
||||||
|
self.brainfunc = 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
|
||||||
|
|
||||||
|
function mobkit.hq_attack(self,prty,tgtobj)
|
||||||
|
local func = function(self)
|
||||||
|
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.8 or tgtobj:get_luaentity().height*0.6
|
||||||
|
if tpos.y+height>pos.y then
|
||||||
|
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 mobkit.hq_liquid_recovery(self,prty) -- scan for nearest land
|
||||||
|
local radius = 1
|
||||||
|
local yaw = 0
|
||||||
|
local func = function(self)
|
||||||
|
if not self.isinliquid then return true end
|
||||||
|
local pos=self.object:get_pos()
|
||||||
|
local vec = minetest.yaw_to_dir(yaw)
|
||||||
|
local pos2 = mobkit.pos_shift(pos,vector.multiply(vec,radius))
|
||||||
|
local height, liquidflag = mobkit.get_terrain_height(pos2)
|
||||||
|
if height and not liquidflag then
|
||||||
|
mobkit.hq_swimto(self,prty,pos2)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
yaw=yaw+pi*0.25
|
||||||
|
if yaw>2*pi then
|
||||||
|
yaw = 0
|
||||||
|
radius=radius+1
|
||||||
|
if radius > self.view_range then
|
||||||
|
self.hp = 0
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.hq_swimto(self,prty,tpos)
|
||||||
|
local func = function(self)
|
||||||
|
-- if not self.isinliquid and mobkit.is_queue_empty_low(self) then return true end
|
||||||
|
if not self.isinliquid and self.isonground then return true end
|
||||||
|
-- local pos = self.object:get_pos()
|
||||||
|
local pos = mobkit.get_stand_pos(self)
|
||||||
|
local y=self.object:get_velocity().y
|
||||||
|
local pos2d = {x=pos.x,y=0,z=pos.z}
|
||||||
|
local dir=vector.normalize(vector.direction(pos2d,tpos))
|
||||||
|
local yaw = minetest.dir_to_yaw(dir)
|
||||||
|
|
||||||
|
if mobkit.timer(self,1) then
|
||||||
|
--perpendicular vectors: {-z,x};{z,-x}
|
||||||
|
local offset=self.collisionbox[1]
|
||||||
|
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
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,prty)
|
||||||
|
end
|
|
@ -0,0 +1,227 @@
|
||||||
|
|
||||||
|
local function execute_queues(self)
|
||||||
|
--Execute hqueue
|
||||||
|
if #self.hqueue > 0 then
|
||||||
|
local func = self.hqueue[1].func
|
||||||
|
if func(self) then
|
||||||
|
table.remove(self.hqueue,1)
|
||||||
|
self.lqueue = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Execute lqueue
|
||||||
|
if #self.lqueue > 0 then
|
||||||
|
local func = self.lqueue[1]
|
||||||
|
if func(self) then
|
||||||
|
table.remove(self.lqueue,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sensors()
|
||||||
|
local timer = 2
|
||||||
|
local pulse = 1
|
||||||
|
return function(self)
|
||||||
|
timer=timer-self.dtime
|
||||||
|
if timer < 0 then
|
||||||
|
|
||||||
|
pulse = pulse + 1 -- do full range every third scan
|
||||||
|
local range = self.view_range
|
||||||
|
if pulse > 2 then
|
||||||
|
pulse = 1
|
||||||
|
else
|
||||||
|
range = self.view_range*0.5
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
--local tim = minetest.get_us_time()
|
||||||
|
self.nearby_objects = minetest.get_objects_inside_radius(pos, range)
|
||||||
|
--minetest.chat_send_all(minetest.get_us_time()-tim)
|
||||||
|
for i,obj in ipairs(self.nearby_objects) do
|
||||||
|
if obj == self.object then
|
||||||
|
table.remove(self.nearby_objects,i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
timer=2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
------------
|
||||||
|
-- CALLBACKS
|
||||||
|
------------
|
||||||
|
|
||||||
|
function mobkit.default_brain(self)
|
||||||
|
if mobkit.is_queue_empty_high(self) then mobkit.hq_roam(self,0) end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.statfunc(self)
|
||||||
|
local tmptab={}
|
||||||
|
tmptab.memory = self.memory
|
||||||
|
tmptab.hp = self.hp
|
||||||
|
tmptab.texture_no = self.texture_no
|
||||||
|
return minetest.serialize(tmptab)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.actfunc(self, staticdata, dtime_s)
|
||||||
|
self.lqueue = {}
|
||||||
|
self.hqueue = {}
|
||||||
|
self.nearby_objects = {}
|
||||||
|
self.nearby_players = {}
|
||||||
|
self.pos_history = {}
|
||||||
|
self.path_dir = 1
|
||||||
|
self.time_total = 0
|
||||||
|
|
||||||
|
local sdata = minetest.deserialize(staticdata)
|
||||||
|
if sdata then
|
||||||
|
for k,v in pairs(sdata) do
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.timeout and self.timeout>0 and dtime_s > self.timeout and next(self.memory)==nil then
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.memory then -- this is the initial activation
|
||||||
|
self.memory = {}
|
||||||
|
|
||||||
|
-- texture variation
|
||||||
|
if #self.textures > 1 then self.texture_no = random(#self.textures) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply texture
|
||||||
|
if self.texture_no then
|
||||||
|
local props = {}
|
||||||
|
props.textures = {self.textures[self.texture_no]}
|
||||||
|
self.object:set_properties(props)
|
||||||
|
end
|
||||||
|
|
||||||
|
--hp
|
||||||
|
self.hp = self.hp or (self.max_hp or 10)
|
||||||
|
--armor
|
||||||
|
if type(self.armor_groups) ~= 'table' then
|
||||||
|
self.armor_groups={}
|
||||||
|
end
|
||||||
|
self.armor_groups.immortal = 1
|
||||||
|
self.object:set_armor_groups(self.armor_groups)
|
||||||
|
|
||||||
|
self.oxygen = self.oxygen or self.lung_capacity
|
||||||
|
self.lastvelocity = {x=0,y=0,z=0}
|
||||||
|
self.height = self.collisionbox[5] - self.collisionbox[2]
|
||||||
|
self.sensefunc=sensors()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mobkit.stepfunc(self,dtime) -- not intended to be modified
|
||||||
|
self.dtime = dtime
|
||||||
|
-- physics comes first
|
||||||
|
-- self.object:set_acceleration({x=0,y=mobkit.gravity,z=0})
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
|
||||||
|
-- if self.lastvelocity.y == vel.y then
|
||||||
|
if abs(self.lastvelocity.y-vel.y)<0.001 then
|
||||||
|
self.isonground = true
|
||||||
|
else
|
||||||
|
self.isonground = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- dumb friction
|
||||||
|
if self.isonground then
|
||||||
|
self.object:set_velocity({x= vel.x> 0.2 and vel.x*mobkit.friction or 0,
|
||||||
|
y=vel.y,
|
||||||
|
z=vel.z > 0.2 and vel.z*mobkit.friction or 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- bounciness
|
||||||
|
if self.springiness and self.springiness > 0 then
|
||||||
|
local vnew = vector.new(vel)
|
||||||
|
|
||||||
|
if not self.collided then -- ugly workaround for inconsistent collisions
|
||||||
|
for _,k in ipairs({'y','z','x'}) do
|
||||||
|
if vel[k]==0 and abs(self.lastvelocity[k])> 0.1 then
|
||||||
|
vnew[k]=-self.lastvelocity[k]*self.springiness
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not vector.equals(vel,vnew) then
|
||||||
|
self.collided = true
|
||||||
|
else
|
||||||
|
if self.collided then
|
||||||
|
vnew = vector.new(self.lastvelocity)
|
||||||
|
end
|
||||||
|
self.collided = false
|
||||||
|
end
|
||||||
|
|
||||||
|
self.object:set_velocity(vnew)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- buoyancy
|
||||||
|
local spos = mobkit.get_stand_pos(self)
|
||||||
|
spos.y = spos.y+0.01
|
||||||
|
-- get surface height
|
||||||
|
-- local surface = mobkit.get_node_pos(spos).y+0.5
|
||||||
|
local surface = nil
|
||||||
|
local snodepos = mobkit.get_node_pos(spos)
|
||||||
|
local surfnode = mobkit.nodeatpos(spos)
|
||||||
|
while surfnode and surfnode.drawtype == 'liquid' do
|
||||||
|
surface = snodepos.y+0.5
|
||||||
|
if surface > spos.y+self.height then break end
|
||||||
|
snodepos.y = snodepos.y+1
|
||||||
|
surfnode = mobkit.nodeatpos(snodepos)
|
||||||
|
end
|
||||||
|
if surface then -- standing in liquid
|
||||||
|
self.isinliquid = true
|
||||||
|
local submergence = min(surface-spos.y,self.height)
|
||||||
|
local balance = self.buoyancy*self.height
|
||||||
|
local buoyacc = mobkit.gravity*((balance - submergence)^2/balance^2*sign(balance - submergence))
|
||||||
|
self.object:set_acceleration({x=-vel.x,y=buoyacc-vel.y*abs(vel.y)*0.7,z=-vel.z})
|
||||||
|
else
|
||||||
|
self.isinliquid = false
|
||||||
|
self.object:set_acceleration({x=0,y=mobkit.gravity,z=0})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- local footnode = mobkit.nodeatpos(spos)
|
||||||
|
-- local headnode
|
||||||
|
-- if footnode and footnode.drawtype == 'liquid' then
|
||||||
|
|
||||||
|
-- vel = self.object:get_velocity()
|
||||||
|
-- headnode = mobkit.nodeatpos(mobkit.pos_shift(spos,{y=self.height or 0})) -- TODO: height may be nil
|
||||||
|
-- local submergence = headnode.drawtype=='liquid'
|
||||||
|
-- and self.buoyancy-1
|
||||||
|
-- or (self.buoyancy*self.height-(1-(spos.y+0.5)%1))^2/(self.buoyancy*self.height)^2*sign(self.buoyancy*self.height-(1-(spos.y+0.5)%1))
|
||||||
|
|
||||||
|
-- local buoyacc = submergence * mobkit.gravity
|
||||||
|
-- self.object:set_acceleration({x=-vel.x,y=buoyacc-vel.y*abs(vel.y)*0.5,z=-vel.z})
|
||||||
|
|
||||||
|
-- end
|
||||||
|
|
||||||
|
if self.brainfunc then
|
||||||
|
-- vitals: fall damage
|
||||||
|
vel = self.object:get_velocity()
|
||||||
|
local velocity_delta = abs(self.lastvelocity.y - vel.y)
|
||||||
|
if velocity_delta > mobkit.safe_velocity then
|
||||||
|
self.hp = self.hp - floor((self.max_hp-100) * min(1, velocity_delta/mobkit.terminal_velocity))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- vitals: oxygen
|
||||||
|
local headnode = mobkit.nodeatpos(mobkit.pos_shift(self.object:get_pos(),{y=self.collisionbox[5]})) -- node at hitbox top
|
||||||
|
if headnode and headnode.drawtype == 'liquid' then
|
||||||
|
self.oxygen = self.oxygen - self.dtime
|
||||||
|
else
|
||||||
|
self.oxygen = self.lung_capacity
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.oxygen <= 0 then self.hp=0 end -- drown
|
||||||
|
|
||||||
|
|
||||||
|
self:sensefunc()
|
||||||
|
self:brainfunc()
|
||||||
|
execute_queues(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.lastvelocity = self.object:get_velocity()
|
||||||
|
self.time_total=self.time_total+self.dtime
|
||||||
|
end
|
|
@ -0,0 +1,477 @@
|
||||||
|
Contents
|
||||||
|
|
||||||
|
1 Concepts
|
||||||
|
1.1 Behavior functions
|
||||||
|
1.1.1 Low level functions
|
||||||
|
1.1.2 High level functions
|
||||||
|
1.1.2.1 Priority
|
||||||
|
1.2 Brain function
|
||||||
|
1.3 Processing diagram
|
||||||
|
1.4 Entity definition
|
||||||
|
1.5 Exposed luaentity members
|
||||||
|
|
||||||
|
2 Reference
|
||||||
|
2.1 Utility functions
|
||||||
|
2.2 Built in behaviors
|
||||||
|
2.2.1 High level behaviors
|
||||||
|
2.2.2 Low level behaviors
|
||||||
|
2.3 Constants and member variables
|
||||||
|
|
||||||
|
-----------
|
||||||
|
1. Concepts
|
||||||
|
-----------
|
||||||
|
|
||||||
|
1.1 Behavior functions
|
||||||
|
|
||||||
|
These are the most fundamental units of code, every action entities can perform is a separate function.
|
||||||
|
There are two types of behaviors:
|
||||||
|
- low level, these govern physical actions and interactions (think moves)
|
||||||
|
- high level, these are logical structures governing low level behaviors in order to perform more complex tasks
|
||||||
|
|
||||||
|
Behaviors run for considerable amount of time, this means the functions are being called repeatedly on consecutive engine steps.
|
||||||
|
Therefore a need for preserving state between calls, this is why they are implemented as closures, see defining conventions for details.
|
||||||
|
|
||||||
|
Behavior functions are active until they finish the job, are removed from the queue or superseded by a higher priority behavior.
|
||||||
|
They signal finished state by returning true, therefore it's very important to carefully design the completion conditions
|
||||||
|
|
||||||
|
For a behavior to begin executing it has to be put on a queue. There are two separate queues, one for low and one for high level behaviors.
|
||||||
|
Queuing is covered by behavour defining conventions
|
||||||
|
|
||||||
|
!!! In simplest scenarios there's no need to code behaviors, much can be achieved using only built-in stuff !!!
|
||||||
|
!!! To start using the api it's enough to learn defining mobs and writing brain functions !!!
|
||||||
|
|
||||||
|
|
||||||
|
1.1.1 Low level behavior functions
|
||||||
|
|
||||||
|
These are physical actions and interactions: steps, jumps, turns etc. here you'll set velocity, yaw, kick off animations and sounds.
|
||||||
|
|
||||||
|
Low level behavior definition:
|
||||||
|
|
||||||
|
function mobkit.lq_bhv1(self,[optional additional persistent parameters]) -- enclosing function
|
||||||
|
... -- optional definitions of additional persistent variables
|
||||||
|
local func=function(self) -- enclosed function, self is mandatory and the only allowed parameter
|
||||||
|
... -- actual function definition, remember to return true eventually
|
||||||
|
end
|
||||||
|
mobkit.queue_low(self,func) -- this will queue the behavior at the time of lq_bhv1 call
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
1.1.2 High level behavior functions
|
||||||
|
|
||||||
|
These are complex tasks like getting to a position, following other objects, hiding, patrolling an area etc.
|
||||||
|
Their job is tracking changes in the environment and managing low level behavior queue accordingly.
|
||||||
|
|
||||||
|
High level behavior definition:
|
||||||
|
|
||||||
|
function mobkit.hq_bhv1(self,priority,[optional additional persistent parameters]) -- enclosing function
|
||||||
|
... -- optional definitions of additional persistent variables
|
||||||
|
local func=function(self) -- enclosed function, self is mandatory and the only allowed parameter
|
||||||
|
... -- actual function definition, remember to return true eventually
|
||||||
|
end
|
||||||
|
mobkit.queue_high(self,func,priority) -- this will queue the behavior at the time of hq_bhv1 call
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
1.1.2.1 Priority
|
||||||
|
|
||||||
|
Unlike low level behaviors which are executed in FIFO order, high level behaviors support prioritization.
|
||||||
|
This concept is essential for making sure the right behavior is active at the right time.
|
||||||
|
Prioritization is what makes it possible to interrupt a task in order to perform a more important one
|
||||||
|
|
||||||
|
The currently executing behavior is always the first in the queue.
|
||||||
|
When a new behavior is placed onto the queue:
|
||||||
|
If the queue is not empty a new behavior is inserted before the first behavior of lower priority if such exists, or last.
|
||||||
|
If the new behavior supersedes the one currently executing, low level queue is purged immediately.
|
||||||
|
|
||||||
|
Common idioms:
|
||||||
|
|
||||||
|
hq_bhv1(self,prty):
|
||||||
|
...
|
||||||
|
hq_bhv2(self,prty) -- bhv1 kicks off bhv2 with equal priority
|
||||||
|
return true -- and ends,
|
||||||
|
-- bhv2 becomes active on the next engine step.
|
||||||
|
|
||||||
|
hq_bhv1(self,prty):
|
||||||
|
...
|
||||||
|
hq_bhv2(self,prty+1) -- bhv1 kicks off bhv2 with higher priority
|
||||||
|
-- bhv2 takes over and when it ends, bhv1 resumes.
|
||||||
|
|
||||||
|
|
||||||
|
Particular prioritization scheme is to be designed by the user according to specific mod requirements.
|
||||||
|
|
||||||
|
|
||||||
|
1.2 Brain function
|
||||||
|
------------------
|
||||||
|
Every mob must have one.
|
||||||
|
Its job is managing high level behavior queue in response to events which are not intercepted by callbacks.
|
||||||
|
Contrary to what the name suggests, these functions needn't necessarily be too complex thanks to their limited responsibilities.
|
||||||
|
|
||||||
|
Typical flow might look like this:
|
||||||
|
|
||||||
|
if mobkit.timer(self,1) then -- returns true approx every second
|
||||||
|
local prty = mobkit.get_queue_priority(self)
|
||||||
|
|
||||||
|
if prty < 20
|
||||||
|
if ... then
|
||||||
|
hq_do_important_stuff(self,20)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if prty < 10 then
|
||||||
|
if ... then
|
||||||
|
hq_do_something_else(self,10)
|
||||||
|
return
|
||||||
|
elseif ... then
|
||||||
|
hq_do_this_instead(self,10)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mobkit.is_queue_empty_high(self) then
|
||||||
|
hq_fool_around(self,0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
1.3 Processing diagram
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
---------------------------------------
|
||||||
|
| PHYSICS |
|
||||||
|
| |
|
||||||
|
| ----------------------- |
|
||||||
|
| | Brain Function | |
|
||||||
|
| ----------------------- |
|
||||||
|
| | |
|
||||||
|
| -----|----------------- |
|
||||||
|
| | V HL Queue | |
|
||||||
|
| | | 1 | 2 | 3 |... | |
|
||||||
|
| ----------------------- |
|
||||||
|
| | |
|
||||||
|
| -----|----------------- |
|
||||||
|
| | V LL Queue | |
|
||||||
|
| | | 1 | 2 | 3 |... | |
|
||||||
|
| ----------------------- |
|
||||||
|
| |
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
Order of execution during an engine step:
|
||||||
|
First comes physics: gravity, buoyancy, friction etc., then the brain function is called.
|
||||||
|
After that, the first behavior on the high level queue, if exists,
|
||||||
|
and the last, the first low level behavior if present.
|
||||||
|
|
||||||
|
1.4 Entity definition
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
minetest.register_entity("mod:name",{
|
||||||
|
|
||||||
|
-- required minetest api props
|
||||||
|
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = true,
|
||||||
|
collisionbox = {...},
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "...",
|
||||||
|
textures = {...},
|
||||||
|
|
||||||
|
|
||||||
|
-- required mobkit props
|
||||||
|
|
||||||
|
timeout = [num], -- entities are removed after this many seconds inactive
|
||||||
|
-- 0 is never
|
||||||
|
-- mobs having memory entries are not affected
|
||||||
|
|
||||||
|
buoyancy = [num], -- (0,1) - portion of collisionbox submerged
|
||||||
|
-- = 1 - controlled buoyancy (fish, submarine)
|
||||||
|
-- > 1 - drowns
|
||||||
|
-- < 0 - MC like water trampolining
|
||||||
|
|
||||||
|
lung_capacity = [num], -- seconds
|
||||||
|
max_hp = [num],
|
||||||
|
on_step = mobkit.stepfunc,
|
||||||
|
on_activate = mobkit.actfunc,
|
||||||
|
get_staticdata = mobkit.statfunc,
|
||||||
|
brainfunc = [function user defined],
|
||||||
|
|
||||||
|
-- optional mobkit props
|
||||||
|
-- or used by built in behaviors
|
||||||
|
|
||||||
|
animation = {
|
||||||
|
[name]={range={x=[num],y=[num]},speed=[num],loop=[bool]}, -- single
|
||||||
|
|
||||||
|
[name]={ -- variant, animations are chosen randomly.
|
||||||
|
{range={x=[num],y=[num]},speed=[num],loop=[bool]},
|
||||||
|
{range={x=[num],y=[num]},speed=[num],loop=[bool]},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
sounds = {
|
||||||
|
[name] = [string filename],
|
||||||
|
...
|
||||||
|
}
|
||||||
|
max_speed = [num], -- m/s
|
||||||
|
jump_height = [num], -- nodes/meters
|
||||||
|
view_range = [num], -- nodes/meters
|
||||||
|
attack={range=[num], -- range is distance between attacker's collision box center
|
||||||
|
damage_groups={fleshy=[num]}}, -- and the tip of the murder weapon in nodes/meters
|
||||||
|
armor_groups = {fleshy=[num]}
|
||||||
|
})
|
||||||
|
|
||||||
|
1.5 Exposed luaentity members
|
||||||
|
|
||||||
|
Some frequently used entity fields to be accessed directly for convenience
|
||||||
|
|
||||||
|
self.dtime - dtime as passed to on_step
|
||||||
|
self.hp - hitpoints
|
||||||
|
self.isonground - true if pos.y remains unchanged for 2 consecutive steps
|
||||||
|
self.isinliquid - true if the node at foot level is drawtype=='liquid'
|
||||||
|
|
||||||
|
------------
|
||||||
|
2. Reference
|
||||||
|
------------
|
||||||
|
|
||||||
|
2.1 Utility Functions
|
||||||
|
|
||||||
|
function mobkit.get_terrain_height(pos,steps)
|
||||||
|
-- recursively search for walkable surface at pos.
|
||||||
|
-- steps (optional) is how far from pos it gives up, expressed in nodes, default 3
|
||||||
|
-- Returns:
|
||||||
|
-- surface height at pos, or nil if not found
|
||||||
|
-- liquid flag: true if found surface is covered with liquid
|
||||||
|
|
||||||
|
function mobkit.timer(self,s)
|
||||||
|
-- returns true approx every s seconds
|
||||||
|
-- used to reduce execution of code that needn't necessarily be done on every engine step
|
||||||
|
|
||||||
|
function mobkit.pos_shift(pos,vec)
|
||||||
|
-- convenience function
|
||||||
|
-- returns pos shifted by vec
|
||||||
|
-- vec needn't have all three components given, absent components are assumed zero.
|
||||||
|
-- e.g pos_shift(pos,{y=1}) is valid
|
||||||
|
|
||||||
|
function mobkit.get_stand_pos(thing)
|
||||||
|
-- returns object pos projected onto the bottom collisionbox face
|
||||||
|
-- thing can be luaentity or objectref.
|
||||||
|
|
||||||
|
function mobkit.nodeatpos(pos)
|
||||||
|
-- convenience function
|
||||||
|
-- returns nodedef or nil if it's an ignore node
|
||||||
|
|
||||||
|
function mobkit.get_node_pos(pos)
|
||||||
|
-- returns center of the node that pos is inside
|
||||||
|
|
||||||
|
function mobkit.get_nodes_in_area(pos1,pos2,[full])
|
||||||
|
-- in basic mode returns a table of unique nodes within area indexed by node
|
||||||
|
-- in full=true mode returns a table of nodes indexed by pos
|
||||||
|
-- works for up to 125 nodes.
|
||||||
|
|
||||||
|
function mobkit.isnear2d(p1,p2,thresh)
|
||||||
|
-- returns true if pos p2 is within a square with center at pos p1 and radius thresh
|
||||||
|
-- y components are ignored
|
||||||
|
|
||||||
|
function mobkit.is_there_yet2d(pos,dir,dest) -- obj positon; facing vector; destination position
|
||||||
|
-- returns true if a position dest is behind position pos according to facing vector dir
|
||||||
|
-- (checks if dest is in the rear half plane as defined by pos and dir)
|
||||||
|
-- y components are ignored
|
||||||
|
|
||||||
|
function mobkit.isnear3d(p1,p2,thresh)
|
||||||
|
-- returns true if pos p2 is within a cube with center at pos p1 and radius thresh
|
||||||
|
|
||||||
|
function mobkit.dir_to_rot(v,rot)
|
||||||
|
-- converts a 3d vector v to rotation like in set_rotation() object method
|
||||||
|
-- rot (optional) is current object rotation
|
||||||
|
|
||||||
|
function mobkit.is_alive(thing)
|
||||||
|
-- non essential, checks if thing exists in the world and is alive
|
||||||
|
-- makes an assumption that luaentities are considered dead when their hp < 100
|
||||||
|
-- thing can be luaentity or objectref.
|
||||||
|
-- used for stored luaentities and objectrefs
|
||||||
|
|
||||||
|
function mobkit.exists(thing)
|
||||||
|
-- checks if thing exists in the world
|
||||||
|
-- thing can be luaentity or objectref.
|
||||||
|
-- used for stored luaentities and objectrefs
|
||||||
|
|
||||||
|
function mobkit.hurt(luaent,dmg)
|
||||||
|
-- decrease luaent.hp by dmg
|
||||||
|
|
||||||
|
function mobkit.heal(luaent,dmg)
|
||||||
|
-- increase luaent.hp by dmg
|
||||||
|
|
||||||
|
function mobkit.get_spawn_pos_abr(dtime,intrvl,radius,chance,reduction)
|
||||||
|
-- returns a potential spawn position at random intervals
|
||||||
|
-- intrvl: avg spawn attempt interval for every player
|
||||||
|
-- radius: spawn distance in nodes, active_block_range*16 is recommended
|
||||||
|
-- chance: (0,1) chance to spawn a mob if there are no other objects in area
|
||||||
|
-- reduction: (0,1) spawn chance is reduced by this factor for every object in range.
|
||||||
|
--usage:
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
local spawnpos = mobkit.get_spawn_pos_abr(...)
|
||||||
|
if spawnpos then
|
||||||
|
... -- mod/game specific logic
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function mobkit.animate(self,anim)
|
||||||
|
-- makes an entity play an animation of name anim, or does nothing if not defined
|
||||||
|
-- anim is string, see entity definition
|
||||||
|
-- does nothing if the same animation is already running
|
||||||
|
|
||||||
|
function mobkit.make_sound(self,sound)
|
||||||
|
-- sound is string, see entity definition
|
||||||
|
-- makes an entity play sound, or does nothing if not defined
|
||||||
|
|
||||||
|
|
||||||
|
-- Memory functions.
|
||||||
|
|
||||||
|
This represents mob long term memory
|
||||||
|
Warning: Stuff in memory is serialized, never try to remember objectrefs or tables referencing them
|
||||||
|
or the engine will crash.
|
||||||
|
|
||||||
|
function mobkit.remember(self,key,val)
|
||||||
|
-- premanently store a key, value pair
|
||||||
|
function mobkit.forget(self,key)
|
||||||
|
-- clears a memory entry
|
||||||
|
function mobkit.recall(self,key)
|
||||||
|
-- returns val associated with key
|
||||||
|
|
||||||
|
-- Queue functions
|
||||||
|
|
||||||
|
function mobkit.queue_high(self,func,priority)
|
||||||
|
-- only for use in behavior definitions, see 1.1.2
|
||||||
|
|
||||||
|
function mobkit.queue_low(self,func)
|
||||||
|
-- only for use in behavior definitions, see 1.1.1
|
||||||
|
|
||||||
|
|
||||||
|
function mobkit.clear_queue_high(self)
|
||||||
|
function mobkit.clear_queue_low(self)
|
||||||
|
|
||||||
|
function mobkit.is_queue_empty_high(self)
|
||||||
|
function mobkit.is_queue_empty_low(self)
|
||||||
|
|
||||||
|
function mobkit.get_queue_priority(self)
|
||||||
|
-- returns the priority of currently running behavior
|
||||||
|
-- this is also the highest of all queued behaviors
|
||||||
|
|
||||||
|
|
||||||
|
-- Use these inside brain functions --
|
||||||
|
|
||||||
|
function mobkit.get_nearby_player(self)
|
||||||
|
-- returns random player if nearby or nil
|
||||||
|
function mobkit.get_nearby_entity(self,name)
|
||||||
|
-- returns random nearby entity of name or nil
|
||||||
|
function mobkit.get_closest_entity(self,name)
|
||||||
|
-- returns closest entity of name or nil
|
||||||
|
|
||||||
|
|
||||||
|
-- Misc
|
||||||
|
|
||||||
|
Neighbors structure represents a node's horizontal neighbors
|
||||||
|
Not essential, used by some built in behaviors
|
||||||
|
Custom behaviors may not need it.
|
||||||
|
|
||||||
|
Neighbor #1 is offset {x=1,z=0}, subsequent numbers go clockwise
|
||||||
|
|
||||||
|
function mobkit.dir2neighbor(dir)
|
||||||
|
-- converts a 3d vector to neighbor number, y component ignored
|
||||||
|
|
||||||
|
function mobkit.neighbor_shift(neighbor,shift)
|
||||||
|
-- get another neighbor number relative to the given, shift: plus is clockwise, minus the opposite
|
||||||
|
-- 1,1 = 2; 1,-2 = 7
|
||||||
|
|
||||||
|
|
||||||
|
2.2 Built in behaviors
|
||||||
|
|
||||||
|
function mobkit.goto_next_waypoint(self,tpos)
|
||||||
|
-- this functions groups common operations making mobs move in a specific direction
|
||||||
|
-- not a behavior itself, but is used by some built in HL behaviors
|
||||||
|
-- which use node by node movement algorithm
|
||||||
|
|
||||||
|
2.2.1 High Level Behaviors --
|
||||||
|
|
||||||
|
function mobkit.hq_roam(self,prty)
|
||||||
|
-- slow random roaming
|
||||||
|
-- never returns
|
||||||
|
|
||||||
|
function mobkit.hq_follow(self,prty,tgtobj)
|
||||||
|
-- follow the tgtobj
|
||||||
|
-- returns if tgtobj becomes inactive
|
||||||
|
|
||||||
|
function mobkit.hq_goto(self,prty,tpos)
|
||||||
|
-- go to tpos position
|
||||||
|
-- returns on arrival
|
||||||
|
|
||||||
|
function mobkit.hq_runfrom(self,prty,tgtobj)
|
||||||
|
-- run away from tgtobj object
|
||||||
|
-- returns when tgtobj far enough
|
||||||
|
|
||||||
|
function mobkit.hq_hunt(self,prty,tgtobj)
|
||||||
|
-- follow tgtobj and when close enough, kick off hq_attack
|
||||||
|
-- returns when tgtobj too far
|
||||||
|
|
||||||
|
function mobkit.hq_warn(self,prty,tgtobj)
|
||||||
|
-- when a tgtobj close by, turn towards them and make the 'warn' sound
|
||||||
|
-- kick off hq_hunt if tgtobj too close or timer expired
|
||||||
|
-- returns when tgtobj moves away
|
||||||
|
|
||||||
|
function mobkit.hq_die(self)
|
||||||
|
-- default death, rotate and remove() after set time
|
||||||
|
|
||||||
|
function mobkit.hq_attack(self,prty,tgtobj)
|
||||||
|
-- default attack, turns towards tgtobj and leaps
|
||||||
|
-- returns when tgtobj out of range
|
||||||
|
|
||||||
|
function mobkit.hq_liquid_recovery(self,prty)
|
||||||
|
-- use when submerged in liquid, scan for nearest land
|
||||||
|
-- if land is found within view_range, kick off hq_swimto
|
||||||
|
-- otherwise die
|
||||||
|
|
||||||
|
function mobkit.hq_swimto(self,prty,tpos)
|
||||||
|
-- swim towards the position tpos, jump if necessary
|
||||||
|
-- returns if standing firmly on dry land
|
||||||
|
|
||||||
|
|
||||||
|
2.2.2 Low Level Behaviors --
|
||||||
|
|
||||||
|
function mobkit.lq_turn2pos(self,tpos)
|
||||||
|
-- gradually turn towards tpos position
|
||||||
|
-- returns when facing tpos
|
||||||
|
|
||||||
|
function mobkit.lq_idle(self,duration)
|
||||||
|
-- do nothing for duration seconds
|
||||||
|
-- set 'stand' animation
|
||||||
|
|
||||||
|
function mobkit.lq_dumbwalk(self,dest,speed_factor)
|
||||||
|
-- simply move towards dest
|
||||||
|
-- set 'walk' animation
|
||||||
|
|
||||||
|
function mobkit.lq_dumbjump(self,height)
|
||||||
|
-- if standing on the ground, jump in the facing direction
|
||||||
|
-- height is relative to feet level
|
||||||
|
-- set 'stand' animation
|
||||||
|
|
||||||
|
function mobkit.lq_freejump(self)
|
||||||
|
-- unconditional jump in the facing direction
|
||||||
|
-- useful e.g for getting out of water
|
||||||
|
-- returns when the apex has been reached
|
||||||
|
|
||||||
|
function mobkit.lq_jumpattack(self,height,target)
|
||||||
|
-- jump towards the target, punch if a hit
|
||||||
|
-- returns after punch or on the ground
|
||||||
|
|
||||||
|
function mobkit.lq_fallover(self)
|
||||||
|
-- gradually rotates Z = 0 to pi/2
|
||||||
|
|
||||||
|
|
||||||
|
2.3 Constants and member variables --
|
||||||
|
|
||||||
|
mobkit.gravity = -9.8
|
||||||
|
mobkit.friction = 0.4 -- inert entities will slow down when in contact with the ground
|
||||||
|
-- the smaller the number, the greater the effect
|
||||||
|
|
||||||
|
self.dtime -- for convenience, dtime as passed to currently executing on_step()
|
||||||
|
self.isonground -- true if y velocity is 0 for at least two succesive steps
|
||||||
|
self.isinliquid -- true if feet submerged in liquid type=source
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue