1787 lines
49 KiB
Lua
1787 lines
49 KiB
Lua
aliveai.is_invisiable=function(ob)
|
|
if ob:is_player() and minetest.check_player_privs(ob:get_player_name(), {aliveai_invisibility=true}) and ob:get_player_control().sneak then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
aliveai.newpos=function(pos,a)
|
|
if not pos then
|
|
return
|
|
elseif pos.object then
|
|
pos=pos.object:get_pos()
|
|
elseif pos:get_pos() then
|
|
pos=pos:get_pos()
|
|
end
|
|
if a and type(a)~="table" then
|
|
return pos
|
|
elseif a then
|
|
return {x=pos.x+(a.x or 0),y=pos.y+(a.y or 0),z=pos.z+(a.z or 0)}
|
|
end
|
|
return {
|
|
x=pos.x,
|
|
z=pos.z,
|
|
y=pos.y,
|
|
xx=function(p,n) return {x=p.x+(n or 0),y=p.y,z=p.z} end,
|
|
yy=function(p,n) return {x=p.x,y=p.y+(n or 0),z=p.z} end,
|
|
zz=function(p,n) return {x=p.x,y=p.y,z=p.z+(n or 0)} end,
|
|
}
|
|
end
|
|
|
|
minetest.register_on_item_eat=function(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
|
if not itemstack then
|
|
return
|
|
end
|
|
local a=itemstack:get_name()
|
|
if not aliveai.food[a] and hp_change>0 and minetest.get_item_group(a,"aliveai_eatable")==0 then
|
|
aliveai.food[a]=hp_change
|
|
aliveai.save("food",aliveai.food)
|
|
local def=minetest.registered_items[a]
|
|
def.groups=def.groups or {}
|
|
def.groups.aliveai_eatable=hp_change
|
|
minetest.override_item(a, def)
|
|
end
|
|
|
|
end
|
|
|
|
minetest.after(0, function()
|
|
local f=aliveai.load("food")
|
|
if f then
|
|
aliveai.food=f
|
|
for i, v in pairs(aliveai.food) do
|
|
local def=minetest.registered_items[i]
|
|
if def then
|
|
def.groups=def.groups or {}
|
|
def.groups.aliveai_eatable=v
|
|
minetest.override_item(i, def)
|
|
end
|
|
end
|
|
else
|
|
aliveai.save("food",aliveai.food)
|
|
end
|
|
|
|
aliveai.respawn_player_point=aliveai.strpos(minetest.settings:get("static_spawnpoint"),1)
|
|
if not aliveai.respawn_player_point or aliveai.respawn_player_point=="" then
|
|
aliveai.respawn_player_point=nil
|
|
minetest.register_on_leaveplayer(function(player)
|
|
if not aliveai.replayerpos then return end
|
|
aliveai.replayerpos[player:get_player_name()]=nil
|
|
end)
|
|
minetest.register_on_respawnplayer(function(player)
|
|
if not aliveai.replayerpos then aliveai.replayerpos={} end
|
|
minetest.after(0, function(player)
|
|
aliveai.replayerpos[player:get_player_name()]=player:get_pos()
|
|
end, player)
|
|
end)
|
|
minetest.register_on_joinplayer(function(player)
|
|
if not aliveai.replayerpos then aliveai.replayerpos={} end
|
|
aliveai.replayerpos[player:get_player_name()]=player:get_pos()
|
|
end)
|
|
end
|
|
|
|
end)
|
|
|
|
aliveai.respawn_player=function(ob)
|
|
if ob:is_player() then
|
|
local n=ob:get_player_name()
|
|
if beds and beds.spawn and beds.spawn[n] then
|
|
ob:set_pos(beds.spawn[n])
|
|
ob:set_hp(20)
|
|
elseif aliveai.respawn_player_point then
|
|
ob:set_pos(aliveai.respawn_player_point)
|
|
ob:set_hp(20)
|
|
elseif aliveai.replayerpos and aliveai.replayerpos[n] then
|
|
ob:set_pos(aliveai.replayerpos[n])
|
|
ob:set_hp(20)
|
|
else
|
|
ob:set_pos({x=0,y=-100,z=0})
|
|
ob:set_hp(0)
|
|
end
|
|
elseif ob:get_luaentity() then
|
|
ob:remove()
|
|
end
|
|
end
|
|
|
|
aliveai.protected=function(pos,name)
|
|
if not (pos and pos.x) then return true end
|
|
name=name or ""
|
|
if type(name)=="string" then
|
|
elseif type(name)=="table" and name.aliveai then
|
|
name=name.botname or ""
|
|
else
|
|
return true
|
|
end
|
|
local m=minetest.get_meta(pos):get_string("aliveai_protected")
|
|
return m~="" and m~=name
|
|
end
|
|
|
|
aliveai.protect=function(pos,a)
|
|
a.name=a.name or "?"
|
|
a.range=a.range or 15
|
|
for i, p in pairs(aliveai.get_nodes(pos,a.range,1,{})) do
|
|
local m=minetest.get_meta(p)
|
|
m:set_string("aliveai_protected",a.name)
|
|
end
|
|
end
|
|
|
|
aliveai.unprotect=function(pos,a)
|
|
a.name=a.name or ""
|
|
a.range=a.range or 15
|
|
for i, p in pairs(aliveai.get_nodes(pos,a.range,1,{})) do
|
|
local m=minetest.get_meta(p)
|
|
m:set_string("aliveai_protected","")
|
|
end
|
|
end
|
|
|
|
aliveai.nan=function(a)
|
|
return (a == math.huge or a == -math.huge or a ~= a) == false and a or 0
|
|
end
|
|
|
|
aliveai.group=function(pos,g)
|
|
if not (pos and g) then return 0 end
|
|
return minetest.get_item_group(minetest.get_node(pos).name,g)
|
|
end
|
|
|
|
aliveai.param2_to_xzyaw=function(a)
|
|
if a and a.x and a.y and a.z then
|
|
a=minetest.get_node(a).param2
|
|
elseif tonumber(a)==nil then
|
|
return 0
|
|
end
|
|
if a==3 then
|
|
return {x=1,y=0,z=0}, 4.71
|
|
elseif a==1 then
|
|
return {x=-1,y=0,z=0}, 1.57
|
|
elseif a==2 then
|
|
return {x=0,y=0,z=1}, 0
|
|
else -- a==0
|
|
return {x=0,y=0,z=-1}, 3.14
|
|
end
|
|
end
|
|
|
|
aliveai.xz_to_param2yaw=function(x,z)
|
|
if x and x==-1 then
|
|
return 3, 1.57
|
|
elseif x and x==1 then
|
|
return 1, 4.71
|
|
elseif z and z==-1 then
|
|
return 2, 3.14
|
|
else -- z==1
|
|
return 0, 0
|
|
end
|
|
end
|
|
|
|
aliveai.def=function(pos,n)
|
|
if not (pos and pos.x and pos.y and pos.z and n and minetest.registered_nodes[minetest.get_node(pos).name]) then return nil end
|
|
return minetest.registered_nodes[minetest.get_node(pos).name][n]
|
|
end
|
|
|
|
aliveai.defnode=function(name,n)
|
|
if minetest.registered_nodes[name] and minetest.registered_nodes[name][n] then
|
|
return minetest.registered_nodes[name][n]
|
|
end
|
|
end
|
|
|
|
aliveai.floating=function(self,f)
|
|
if f and f==true then
|
|
f=1
|
|
end
|
|
if f==1 then
|
|
local v=self.object:get_velocity()
|
|
local y=0
|
|
if v.y<0 then y=v.y/10 end
|
|
self.floating=1
|
|
self.object:set_acceleration({x=0,y=0,z =0})
|
|
self.object:set_velocity({x=v.x,y=y,z=v.z})
|
|
self.path=nil
|
|
else
|
|
self.floating=0
|
|
self.object:set_acceleration({x=0,y=-aliveai.gravity,z =0})
|
|
self.object:set_velocity({x=0,y=-5,z =0})
|
|
end
|
|
return self
|
|
end
|
|
|
|
aliveai.die=function(self)
|
|
self.hp=0
|
|
self.object:set_hp(0)
|
|
aliveai.punch(self,self.object,self.hp_max)
|
|
return self
|
|
end
|
|
|
|
|
|
aliveai.kill=function(self)
|
|
self.destroy=1
|
|
self.hp=0
|
|
self.object:set_hp(0)
|
|
self.on_step=nil
|
|
aliveai.punch(self,self.object,9000)
|
|
return self
|
|
end
|
|
|
|
aliveai.team_load=function()
|
|
local t=aliveai.load("team_player")
|
|
if not t then return end
|
|
for i, v in pairs(t) do
|
|
aliveai.team_player[i]=v
|
|
end
|
|
end
|
|
|
|
aliveai.team=function(ob,change_team)
|
|
if type(ob)~="userdata" then return "" end
|
|
if change_team and type(change_team)~="string" then return "" end
|
|
if ob:is_player() then
|
|
local name=ob:get_player_name()
|
|
if change_team then
|
|
aliveai.team_player[name]=change_team
|
|
aliveai.save("team_player",aliveai.team_player)
|
|
minetest.chat_send_player(name, "You are now a member of team " .. change_team)
|
|
return
|
|
end
|
|
local t=aliveai.team_player[name]
|
|
if not t then t=aliveai.default_team end
|
|
return t
|
|
end
|
|
|
|
local en=ob:get_luaentity()
|
|
|
|
if en and change_team then
|
|
en.team=change_team
|
|
elseif aliveai.is_bot(ob) then
|
|
return en.team
|
|
elseif en and en.team then
|
|
return en.team
|
|
elseif en and en.type then
|
|
return en.type
|
|
elseif en and not en.type then
|
|
return ""
|
|
end
|
|
return
|
|
end
|
|
|
|
aliveai.add_mine=function(self,nodes,num,need)
|
|
num=num or 1
|
|
if not self.mine then
|
|
self.mine={target={},status="search"}
|
|
self.ignoremineitem=""
|
|
self.ignoreminetime=0
|
|
self.ignoreminechange=0
|
|
self.ignoreminetimer=200
|
|
self.taskstep=0
|
|
aliveai.rndwalk(self,false)
|
|
end
|
|
local search1
|
|
if type(need)=="string" then
|
|
search1=need
|
|
end
|
|
for i, v in pairs(nodes) do
|
|
local search2
|
|
if minetest.registered_nodes[v] then search2=v end
|
|
if search1 then
|
|
search2=search1
|
|
elseif need and need[i] then
|
|
search2=need[i]
|
|
end
|
|
aliveai.newneed(self,search2,num,v)
|
|
end
|
|
|
|
end
|
|
|
|
aliveai.exit_mine=function(self)
|
|
self.mine=nil
|
|
self.need=nil
|
|
self.ignoremineitem=nil
|
|
self.ignoreminetime=nil
|
|
self.ignoreminechange=nil
|
|
self.ignoreminetimer=nil
|
|
self.taskstep=0
|
|
aliveai.rndwalk(self)
|
|
self.done=""
|
|
end
|
|
|
|
aliveai.random=function(a,b)
|
|
if type(b)~="number" or type(a)~="number" then
|
|
a=0
|
|
b=1
|
|
end
|
|
if a>=b then b=a+0.1 end
|
|
return math.random(a,b)
|
|
end
|
|
|
|
aliveai.is_bot=function(ob)
|
|
return (ob and ob:get_luaentity() and ob:get_luaentity().botname and ob:get_luaentity().aliveai)
|
|
end
|
|
|
|
aliveai.get_bot_name=function(ob)
|
|
if not (ob and ob:get_luaentity() and ob:get_luaentity().aliveai) then return "" end
|
|
return ob:get_luaentity().botname
|
|
end
|
|
|
|
aliveai.same_bot=function(self,ob)
|
|
if not (ob and ob:get_luaentity() and ob:get_luaentity().aliveai) then return false end
|
|
return ob:get_luaentity().botname==self.botname
|
|
end
|
|
|
|
aliveai.get_bot_by_name=function(name)
|
|
for i,v in pairs(aliveai.active) do
|
|
if v:get_luaentity() and v:get_luaentity().botname==name then
|
|
return v
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
aliveai.lookaround=function(self)
|
|
if not self.isrnd then return end
|
|
if self.attention_path then
|
|
aliveai.path(self)
|
|
if self.done=="path" or (math.random(1,5)==1 and aliveai.distance(self,self.attention_path)<self.arm and aliveai.visiable(self,self.attention_path)) then
|
|
aliveai.exitpath(self)
|
|
if self.attention_pathkind==3 then
|
|
aliveai.lookat(self,self.attention_path)
|
|
aliveai.dig(self,self.attention_path)
|
|
end
|
|
self.attention_pathkind=nil
|
|
self.attention_path=nil
|
|
end
|
|
return self
|
|
end
|
|
if math.random(1,10)==1 then
|
|
aliveai.showstatus(self,"look around",1)
|
|
aliveai.stand(self)
|
|
local nodes=aliveai.get_nodes(self,5,2)
|
|
for _, nodepos in ipairs(nodes) do
|
|
if nodepos and aliveai.visiable(self,nodepos) and aliveai.viewfield(self,nodepos) then
|
|
local n=minetest.get_node(nodepos).name
|
|
if self.attention_nodes[n] then
|
|
if self.attention_nodes[n]==1 and math.random(1,5)==1 then
|
|
aliveai.lookat(self,nodepos)
|
|
elseif self.attention_nodes[n]>1 and (self.attention_nodes[n]==3 or math.random(1,3)==1) then
|
|
local upos={x=nodepos.x,y=nodepos.y+1,z=nodepos.z}
|
|
local pa=aliveai.neartarget(self,aliveai.roundpos(self.object:get_pos()))
|
|
if pa then
|
|
local p=aliveai.creatpath(self,pa,upos)
|
|
if p then
|
|
self.path=p
|
|
self.attention_path=nodepos
|
|
self.attention_pathkind=self.attention_nodes[n]
|
|
return self
|
|
end
|
|
end
|
|
elseif self.attention_nodes[n]==-1 and math.random(1,1)==1 then
|
|
self.object:set_yaw(math.random(0,6.28))
|
|
aliveai.walk(self)
|
|
elseif math.random(1,20)==1 then
|
|
aliveai.lookat(self,nodepos)
|
|
end
|
|
if self.attention_nodes[n]~=0 and math.random(1,100)==1 then
|
|
self.attention_nodes[n]=0
|
|
end
|
|
else
|
|
local r=math.random(0,10)
|
|
self.attention_nodes[n]=0
|
|
if r==10 then
|
|
self.attention_nodes[n]=3
|
|
aliveai.lookat(self,nodepos)
|
|
elseif r==9 then
|
|
self.attention_nodes[n]=2
|
|
aliveai.lookat(self,nodepos)
|
|
elseif r==8 then
|
|
self.attention_nodes[n]=1
|
|
aliveai.lookat(self,nodepos)
|
|
elseif r==7 then
|
|
self.attention_nodes[n]=-1
|
|
elseif r<5 then
|
|
aliveai.lookat(self,nodepos)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
aliveai.get_nodes=function(self,radio,dencity,filter)
|
|
if not self then return end
|
|
radio=radio or 25
|
|
dencity=dencity or 5
|
|
filter=filter or {"default:dirt","default:stone","air"}
|
|
local pos
|
|
local rpos={}
|
|
local filter2={}
|
|
if not self.object and self.x and self.y and self.z then
|
|
pos=self
|
|
else
|
|
pos=self.object:get_pos()
|
|
end
|
|
pos=aliveai.roundpos(pos)
|
|
for _, nod in ipairs(filter) do
|
|
table.insert(filter2,minetest.get_content_id(nod))
|
|
end
|
|
local pos1 = vector.subtract(pos, 1)
|
|
local pos2 = vector.add(pos, radio)
|
|
local vox = minetest.get_voxel_manip()
|
|
local min, max = vox:read_from_map(pos1, pos2)
|
|
local area = VoxelArea:new({MinEdge = min, MaxEdge = max})
|
|
local data = vox:get_data()
|
|
for z = -radio, radio do
|
|
for y = -radio, radio do
|
|
for x = -radio, radio do
|
|
local p={x=pos.x+x,y=pos.y+y,z=pos.z+z}
|
|
local v = area:index(p.x,p.y,p.z)
|
|
if math.random(1,dencity)==1 then
|
|
local nf=true
|
|
for _, nod in ipairs(filter2) do
|
|
if not data[v] or data[v]==nod then
|
|
nf=false
|
|
break
|
|
end
|
|
end
|
|
if nf==true then
|
|
table.insert(rpos,p)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return rpos
|
|
end
|
|
|
|
aliveai.random_pos=function(self,Min,Max)
|
|
if not self then return end
|
|
Min=Min or 10
|
|
Max=Max or 25
|
|
local pos
|
|
|
|
local rnd_pos
|
|
local rnd_d=0
|
|
|
|
if not self.object and self.x and self.y and self.z then
|
|
pos=self
|
|
else
|
|
pos=self.object:get_pos()
|
|
end
|
|
pos=aliveai.roundpos(pos)
|
|
|
|
local air=minetest.get_content_id("air")
|
|
local pos1 = vector.subtract(pos, Min)
|
|
local pos2 = vector.add(pos, Max)
|
|
local vox = minetest.get_voxel_manip()
|
|
local min, max = vox:read_from_map(pos1, pos2)
|
|
local area = VoxelArea:new({MinEdge = min, MaxEdge = max})
|
|
local data = vox:get_data()
|
|
for z = -Max, Max do
|
|
for y = -Max, Max do
|
|
for x = -Max, Max do
|
|
local v = area:index(pos.x+x,pos.y+y,pos.z+z)
|
|
local p={x=pos.x+x,y=pos.y+y-1,z=pos.z+z}
|
|
local n=minetest.registered_nodes[minetest.get_node(p).name]
|
|
if data[v]==air and n and n.walkable and math.random(1,10)==1 then
|
|
local a=true
|
|
for i=1,3,1 do
|
|
local p2={x=pos.x+x,y=pos.y+y+i,z=pos.z+z}
|
|
local n=minetest.registered_nodes[minetest.get_node(p2).name]
|
|
if not n or n.walkable then a=false end
|
|
end
|
|
local d=aliveai.distance(pos,p)
|
|
if a and d>rnd_d and d<=Max then
|
|
p.y=p.y+2
|
|
rnd_pos=p
|
|
rnd_d=d
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return rnd_pos,rnd_d
|
|
end
|
|
|
|
aliveai.gethp=function(ob,even_dead)
|
|
if not (ob and ob:get_pos()) then
|
|
return 0
|
|
elseif ob:is_player() then
|
|
return ob:get_hp()
|
|
end
|
|
local en = ob:get_luaentity()
|
|
return en and ((even_dead and en.aliveai and en.dead and en.hp) or (en.aliveai and en.dead and 0) or en.hp or en.health) or ob:get_hp() or 0
|
|
end
|
|
|
|
aliveai.showtext=function(self,text,color)
|
|
self.delstatus=math.random(0,1000)
|
|
local del=self.delstatus
|
|
color=color or "ff0000"
|
|
self.object:set_properties({nametag=text,nametag_color="#" .. color})
|
|
minetest.after(1.5, function(self,del)
|
|
if self and self.object and self.delstatus==del then
|
|
if self.namecolor~="" then
|
|
self.object:set_properties({nametag=self.botname,nametag_color="#" .. self.namecolor})
|
|
else
|
|
self.object:set_properties({nametag="",nametag_color=""})
|
|
end
|
|
end
|
|
end, self,del)
|
|
return self
|
|
end
|
|
|
|
aliveai.showhp=function(self,p)
|
|
local color="ff0000"
|
|
if p then color="00ff00" end
|
|
aliveai.showtext(self,self.object:get_hp() .." / " .. self.hp_max,color)
|
|
return self
|
|
end
|
|
|
|
aliveai.get_dir=function(self,pos2)
|
|
local pos1
|
|
if self.x and self.y and self.z then
|
|
pos1=self
|
|
elseif self.object then
|
|
pos1=self.object:get_pos()
|
|
else
|
|
return {x=0,y=0,z=0}
|
|
end
|
|
if pos2 and not (pos2.x and pos2.y and pos2.z) then
|
|
pos2=pos2:get_pos()
|
|
end
|
|
if not (pos2 and pos2.x) then
|
|
return {x=0,y=0,z=0}
|
|
end
|
|
local d=math.floor(aliveai.distance(pos1,pos2)+0.5)
|
|
return {x=(pos1.x-pos2.x)/-d,y=(pos1.y-pos2.y)/-d,z=(pos1.z-pos2.z)/-d}
|
|
end
|
|
|
|
aliveai.timer=function(self)
|
|
if self.type=="npc" then return end
|
|
if not self.lifetimer or self.fly or self.come or self.fight then self.lifetimer=aliveai.lifetimer end
|
|
self.lifetimer=self.lifetimer-1
|
|
if self.lifetimer>0 then return end
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), self.distance)) do
|
|
local en=ob:get_luaentity()
|
|
if ob:is_player() or (en and en.type and en.type~="" and ((en.type~=self.type) or en.team and en.team~=self.team)) then
|
|
self.lifetimer=nil
|
|
return
|
|
end
|
|
end
|
|
aliveai.showstatus(self," - removed")
|
|
self.object:remove()
|
|
return self
|
|
end
|
|
|
|
|
|
|
|
aliveai.creatpath=function(self,pos1,pos2,d,notadvanced)
|
|
|
|
if aliveai.botdelay(self,1) then return nil end
|
|
|
|
|
|
if aliveai.max_path_delay>aliveai.max_path_delay_time then
|
|
print("Pathfinding delay:" .. aliveai.max_path_delay)
|
|
return nil
|
|
elseif aliveai.max_path_s>10 then
|
|
return
|
|
end
|
|
pos1=aliveai.roundpos(pos1)
|
|
pos2=aliveai.roundpos(pos2)
|
|
d=d or self.distance
|
|
|
|
if self.floating==1 then
|
|
if aliveai.visiable(pos1,pos2) then
|
|
local d2=aliveai.distance(pos1,pos2)
|
|
local dir={x=(pos1.x-pos2.x)/-d2,y=(pos1.y-pos2.y)/-d2,z=(pos1.z-pos2.z)/-d2}
|
|
local p={}
|
|
for i=1,d2,1 do
|
|
table.insert(p,aliveai.roundpos({x=pos1.x+(dir.x*i),y=pos1.y+(dir.y*i),z=pos1.z+(dir.z*i)}))
|
|
end
|
|
aliveai.showpath(p,1,true)
|
|
return p
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local path_delay=os.clock({sec=0})
|
|
local p=minetest.find_path(pos1,pos2, self.distance, 2, self.avoidy,"Dijkstra")
|
|
if aliveai.pathdelays(self,path_delay) then return nil end
|
|
|
|
if aliveai.botdelay(self,1) then return nil end
|
|
|
|
if not notadvanced then
|
|
p=p or aliveai.createpathbyladder(self,pos1,pos2,d)
|
|
p=p or aliveai.createpathbytower(self,pos1,pos2)
|
|
p=p or aliveai.createpathbybridge(self,pos1,pos2)
|
|
end
|
|
if aliveai.botdelay(self,1) then return nil end
|
|
|
|
aliveai.showpath(p,1,true)
|
|
if aliveai.pathdelays(self,path_delay) then return nil end
|
|
return p
|
|
end
|
|
|
|
aliveai.pathdelays=function(self,delay)
|
|
aliveai.max_path_s=aliveai.max_path_s+1
|
|
delay=os.clock({sec=0})-delay
|
|
aliveai.max_path_delay=aliveai.max_path_delay+delay
|
|
if delay>2 or (self.max_path_delay and self.max_path_delay>2) then
|
|
if self.max_path_delay and self.max_path_delay>2 then delay=self.max_path_delay end
|
|
self.on_step=nil
|
|
self.object:set_hp(0)
|
|
self.hp=0
|
|
aliveai.kill(self)
|
|
print("delay: " .. delay .. " destroyed " .. self.botname)
|
|
return self
|
|
else
|
|
if not self.max_path_delay or self.max_path_delay<0 then
|
|
self.max_path_delay=0
|
|
end
|
|
self.max_path_delay=(self.max_path_delay+delay) - 0.1
|
|
if delay>1 then return self end
|
|
end
|
|
end
|
|
|
|
aliveai.createpathbybridge=function(self,pos1,pos2)
|
|
local path
|
|
if aliveai.visiable(pos1,pos2) then
|
|
aliveai.showstatus(self,"bridge path")
|
|
local n=0
|
|
path={}
|
|
local v = {x = pos1.x - pos2.x, y = pos1.y - pos2.y-1, z = pos1.z - pos2.z}
|
|
v=aliveai.roundpos(v)
|
|
local amount = (v.x ^ 2 + v.y ^ 2 + v.z ^ 2) ^ 0.5
|
|
local d=math.floor(math.sqrt((pos1.x-pos2.x)*(pos1.x-pos2.x) + (pos1.y-pos2.y)*(pos1.y-pos2.y)+(pos1.z-pos2.z)*(pos1.z-pos2.z)))
|
|
|
|
local arm=math.floor(self.arm)
|
|
local snb=0
|
|
local sb=(arm-1)*d
|
|
|
|
v.y=v.y-1
|
|
v.x = (v.x / amount)*-1
|
|
v.y = (v.y / amount)*-1
|
|
v.z = (v.z / amount)*-1
|
|
for i=0,d,1 do
|
|
local p={x=pos1.x+(v.x*i),y=pos1.y+v.y-1,z=pos1.z+(v.z*i)}
|
|
local node=minetest.get_node(p)
|
|
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].walkable==false then
|
|
p=aliveai.roundpos(p)
|
|
table.insert(path,p)
|
|
aliveai.showpath(p,1)
|
|
n=n+1
|
|
for ii=1,arm,1 do
|
|
local dp={x=p.x,y=p.y-ii,z=p.z}
|
|
local node2=minetest.registered_nodes[minetest.get_node(dp).name]
|
|
if node2 and node2.damage_per_second>0 then
|
|
ii=arm
|
|
elseif node2 and node2.walkable then
|
|
snb=snb+1
|
|
if snb>=sb then return nil end
|
|
ii=arm
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for i, v in pairs(aliveai.basics) do
|
|
if aliveai.invhave(self,i,n) then
|
|
self.path_bridge=i
|
|
break
|
|
end
|
|
end
|
|
if not self.path_bridge then
|
|
local nn=0
|
|
for i, v in pairs(self.inv) do
|
|
if minetest.registered_nodes[i] and minetest.registered_nodes[i].walkable then
|
|
nn=nn+v
|
|
if nn>=n then
|
|
self.path_bridge=""
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if not self.path_bridge then return nil end
|
|
end
|
|
return path
|
|
end
|
|
|
|
|
|
aliveai.createpathbytower=function(self,pos1,pos2)
|
|
local d=self.distance
|
|
local tp
|
|
local new_path
|
|
local n=0
|
|
local tmp=aliveai.neartarget(self,pos2,-1,-d)
|
|
aliveai.showstatus(self,"tower path")
|
|
if tmp and aliveai.visiable(self,tmp) then
|
|
tp={}
|
|
table.insert(tp,tmp)
|
|
local tmp2=aliveai.neartarget(self,pos2,-1,2,1)
|
|
if tmp2 then
|
|
table.insert(tp,tmp2)
|
|
end
|
|
table.insert(tp,pos2)
|
|
elseif tmp then
|
|
local tp1=minetest.find_path(pos1,tmp, d, 2, self.avoidy,"Dijkstra")
|
|
if tp1 then
|
|
tp=tp1
|
|
table.insert(tp,pos2)
|
|
end
|
|
else
|
|
local tp1=minetest.find_path(pos1,pos2, d, d-1, self.avoidy,"Dijkstra")
|
|
if tp1 then
|
|
tp=tp1
|
|
table.insert(tp,pos2)
|
|
end
|
|
end
|
|
if tp then
|
|
local p
|
|
local po
|
|
new_path={}
|
|
for i, p in pairs(tp) do
|
|
if po and po.y<p.y then
|
|
for i=1, p.y-po.y-1,1 do
|
|
n=n+1
|
|
local po2={x=po.x,y=po.y+i,z=po.z}
|
|
table.insert(new_path, po2)
|
|
aliveai.showpath(po2,2)
|
|
end
|
|
else
|
|
table.insert(new_path, p)
|
|
aliveai.showpath(p,1)
|
|
end
|
|
po={x=p.x,y=p.y,z=p.z}
|
|
end
|
|
|
|
for i, v in pairs(aliveai.basics) do
|
|
if aliveai.invhave(self,i,n) then
|
|
self.path_tower=i
|
|
break
|
|
end
|
|
end
|
|
if not self.path_tower then
|
|
local nn=0
|
|
for i, v in pairs(self.inv) do
|
|
if minetest.registered_nodes[i] and minetest.registered_nodes[i].walkable then
|
|
nn=nn+v
|
|
if nn>=n then
|
|
self.path_tower=""
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return new_path
|
|
end
|
|
|
|
|
|
aliveai.createpathbyladder=function(self,pos1,pos2,d)
|
|
if pos2.y<pos1.y or pos2.y-pos1.y<self.arm then return nil end
|
|
d=d or self.distance
|
|
local np=minetest.find_node_near(pos2, d,aliveai.ladders)
|
|
if np then np=aliveai.neartarget(self,np,1,0) end
|
|
if np then
|
|
aliveai.showstatus(self,"ladder path")
|
|
aliveai.showpath(np,3)
|
|
local np2=minetest.find_node_near(pos1, d,aliveai.ladders)
|
|
if np2 and aliveai.visiable(self,np2) then
|
|
aliveai.showpath(np2,2)
|
|
local np3=minetest.find_path(pos1,np2, d, 2, self.avoidy,"Dijkstra")
|
|
aliveai.showpath(np3,1,true)
|
|
if np3 then
|
|
local y1=math.floor(pos1.y+0.5)
|
|
for i=y1,np.y,1 do
|
|
local pos3={x=np2.x,y=i,z=np2.z}
|
|
aliveai.showpath(pos3,1)
|
|
table.insert(np3, pos3)
|
|
end
|
|
table.insert(np3, np)
|
|
return np3
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
|
|
|
|
aliveai.buildpath=function(self,need)
|
|
if self.house=="" then
|
|
aliveai.generate_house(self)
|
|
elseif string.find(self.house,"+++")~=nil then
|
|
local house1=self.house.split(self.house,"+++")
|
|
self.house=house1[2]
|
|
local v=house1[1].split(house1[1]," ")
|
|
local vx=tonumber(v[1])
|
|
local vy=tonumber(v[2])
|
|
local vz=tonumber(v[3])
|
|
if vx and vy and vz then
|
|
self.build_x=vx
|
|
self.build_y=vy
|
|
self.build_z=vz
|
|
else
|
|
print("aliveai: buildpath failed missing vector(s)")
|
|
self.need=nil
|
|
self.build_x=1
|
|
self.build_y=1
|
|
self.build_z=1
|
|
self.house=""
|
|
self.task="."
|
|
return self
|
|
end
|
|
end
|
|
local building=self.house
|
|
local bas=building.split(building,"+")
|
|
if not bas or not bas[2] then
|
|
print("aliveai: buildpath failed (no +)")
|
|
return nil
|
|
end
|
|
if need then
|
|
local need0={}
|
|
local need1=bas[1].split(bas[1],"!")
|
|
for _, s1 in ipairs(need1) do
|
|
local s2=s1.split(s1," ")
|
|
if s2[1]==nil or s2[2]==nil then
|
|
print("aliveai: need failed (node or number)")
|
|
return nil
|
|
end
|
|
aliveai.newneed(self,s2[1],tonumber(s2[2]))
|
|
end
|
|
return self
|
|
end
|
|
local build={node={},x=self.build_x,y=self.build_y,z=self.build_z}
|
|
build.node={}
|
|
local path={}
|
|
local n=1
|
|
local d1=bas[2].split(bas[2],"!")
|
|
for _, s1 in ipairs(d1) do
|
|
local s2=s1.split(s1," ")
|
|
if s2[1]==nil or s2[2]==nil then
|
|
print("aliveai: buildpath failed (node or number)")
|
|
return nil
|
|
end
|
|
local s2n=tonumber(s2[2])
|
|
for s3=1,s2n,1 do
|
|
build.node[n]=s2[1]
|
|
n=n+1
|
|
end
|
|
end
|
|
local dx=(build.x/2)*-1
|
|
local dz=(build.z/2)*-1
|
|
n=0
|
|
local nn=0
|
|
local pos=self.object:get_pos()
|
|
|
|
|
|
if self.build_pos and self.build_pos~="" then
|
|
pos=self.build_pos
|
|
end
|
|
|
|
aliveai.showstatus(self,"create (house) build path")
|
|
for y=0,build.y,1 do
|
|
for x=0,build.x,1 do
|
|
for z=0,build.z,1 do
|
|
nn=nn+1
|
|
if build.node[nn]~="air" and build.node[nn]~=nil then
|
|
local p={x=pos.x+x+dx,y=pos.y+y,z=pos.z+z+dz}
|
|
if not minetest.get_node(p) or minetest.is_protected(p,"") then
|
|
return nil
|
|
end
|
|
n=n+1
|
|
path[n]={}
|
|
path[n].pos=aliveai.roundpos(p)
|
|
path[n].node=build.node[nn]
|
|
aliveai.showpath(p,2)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return path
|
|
end
|
|
|
|
aliveai.lookforfreespace=function(pos,xzstartdis,xzdis,xz,y)
|
|
for i=xzstartdis,xzdis,1 do
|
|
local p1={x=pos.x+i,y=pos.y,z=pos.z}
|
|
local p2={x=pos.x-i,y=pos.y,z=pos.z}
|
|
local p3={x=pos.x,y=pos.y,z=pos.z+i}
|
|
local p4={x=pos.x,y=pos.y,z=pos.z-i}
|
|
aliveai.showpath({p1,p2,p3,p4},2,true)
|
|
if minetest.get_node(p1) and minetest.get_node(p1).name=="air" then
|
|
if aliveai.checkarea(p1,"air",3,1) and aliveai.checkarea(p1,"air",xz,y) then
|
|
return p1
|
|
end
|
|
end
|
|
if minetest.get_node(p2) and minetest.get_node(p2).name=="air" then
|
|
if aliveai.checkarea(p2,"air",3,1) and aliveai.checkarea(p2,"air",xz,y) then
|
|
return p2
|
|
end
|
|
end
|
|
if minetest.get_node(p3) and minetest.get_node(p3).name=="air" then
|
|
if aliveai.checkarea(p3,"air",3,1) and aliveai.checkarea(p3,"air",xz,y) then
|
|
return p3
|
|
end
|
|
end
|
|
if minetest.get_node(p4) and minetest.get_node(p4).name=="air" then
|
|
if aliveai.checkarea(p4,"air",3,1) and aliveai.checkarea(p4,"air",xz,y) then
|
|
return p4
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
aliveai.checkarea=function(pos,node_name,pxz,py)
|
|
if not pxz then return end
|
|
local pxz2=(pxz/2)*-1
|
|
local air= "air"==node_name
|
|
for y=0,py,1 do
|
|
for x=0,pxz,1 do
|
|
for z=0,pxz,1 do
|
|
local p={x=pos.x+x+pxz2,y=pos.y+y,z=pos.z+z+pxz2}
|
|
local node=minetest.get_node(p)
|
|
if not node or (air and minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].buildable_to==false and node.name~=node_name) or minetest.is_protected(p,"") then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return true
|
|
|
|
end
|
|
|
|
aliveai.rndwalk=function(self,toogle)
|
|
if toogle==nil then toogle=true end
|
|
self.isrnd=toogle
|
|
if toogle==false then
|
|
self.falllook=nil
|
|
return self
|
|
end
|
|
|
|
self.on_random_walk(self)
|
|
local rnd=math.random(0,7)
|
|
--falllook
|
|
if self.falllook then
|
|
self.falllook.times=self.falllook.times-1
|
|
if not self.falllook.ignore then
|
|
local r=aliveai.random(1, self.falllook.n)
|
|
if rnd<2 then
|
|
self.object:set_yaw(aliveai.nan(self.falllook[r]))
|
|
aliveai.stand(self)
|
|
elseif rnd==2 then
|
|
local pos=self.object:get_pos()
|
|
local rndpos
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(pos, self.distance)) do
|
|
local en = ob:get_luaentity()
|
|
if not (en and en.botname == self.botname) then
|
|
rndpos=ob:get_pos()
|
|
if math.random(1,3)==1 then break end
|
|
end
|
|
end
|
|
if rndpos then
|
|
aliveai.lookat(self,rndpos)
|
|
aliveai.stand(self)
|
|
end
|
|
else
|
|
self.object:set_yaw(aliveai.nan(self.falllook[r]))
|
|
aliveai.walk(self)
|
|
end
|
|
if self.falllook.times<=0 then
|
|
self.falllook=nil
|
|
aliveai.falling(self)
|
|
end
|
|
return self
|
|
end
|
|
if self.falllook.times<=0 then self.falllook=nil end
|
|
end
|
|
-- normal rnd
|
|
|
|
if math.random(1,200)==1 then aliveai.sayrnd(self,"mine") end
|
|
|
|
if rnd==0 then
|
|
aliveai.lookat(self,math.random(0,6.28),true)
|
|
aliveai.stand(self)
|
|
elseif rnd==1 then
|
|
aliveai.stand(self)
|
|
elseif rnd==2 then
|
|
local pos=self.object:get_pos()
|
|
if self.staring then
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(pos, self.arm)) do
|
|
if math.random(1,2)==1 and ob and ob:get_pos() and aliveai.visiable(self,ob:get_pos()) and ((aliveai.get_bot_name(ob)==self.staring.name) or (ob:is_player() and self.staring.name==ob:get_player_name())) then
|
|
if self.staring.step>2 then
|
|
self.temper=1
|
|
self.fight=ob
|
|
aliveai.sayrnd(self,"come here")
|
|
self.on_detect_enemy(self,self.fight)
|
|
self.staring=nil
|
|
return self
|
|
end
|
|
self.staring.step=self.staring.step+1
|
|
aliveai.lookat(self,ob:get_pos(),true)
|
|
aliveai.sayrnd(self,"what are you staring at?")
|
|
return self
|
|
end
|
|
end
|
|
elseif math.random(1,4)==1 and (self.annoyed_by_staring==1 or self.stealing==1) then
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(pos, self.arm)) do
|
|
if ob and ob:get_pos() and aliveai.visiable(self,ob:get_pos()) then
|
|
if self.stealing==1 and aliveai.random(1,self.steal_chance)==1 then
|
|
aliveai.steal(self,ob)
|
|
return self
|
|
end
|
|
if self.annoyed_by_staring==1 and self.mood<0 and math.random(1,2)==1 and not aliveai.same_bot(self,ob) then
|
|
self.staring={}
|
|
if ob:is_player() then
|
|
self.staring.name=ob:get_player_name()
|
|
elseif aliveai.is_bot(ob) then
|
|
self.staring.name=ob:get_luaentity().botname
|
|
else
|
|
self.staring.name=ob:get_luaentity().name
|
|
end
|
|
self.staring.step=1
|
|
aliveai.lookat(self,ob:get_pos(),true)
|
|
return self
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.staring=nil
|
|
local rndpos
|
|
local obb
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(pos, self.distance)) do
|
|
if ob and ob:get_pos() and aliveai.viewfield(self,ob) then
|
|
rndpos=ob:get_pos()
|
|
obb=ob
|
|
if math.random(1,3)==1 then break end
|
|
end
|
|
end
|
|
if rndpos then
|
|
if obb:is_player() then
|
|
local n=minetest.registered_nodes[minetest.get_node({x=rndpos.x,y=rndpos.y-1,z=rndpos.z}).name]
|
|
if n and not (n.walkable or n.climbable or n.liquid_viscosity>0) and aliveai.team(obb)==self.team then
|
|
aliveai.sayrnd(self,"its flying!","",true)
|
|
end
|
|
end
|
|
aliveai.lookat(self,rndpos)
|
|
aliveai.stand(self)
|
|
end
|
|
elseif rnd<4 then
|
|
aliveai.lookat(self,math.random(0,6.28),true,true)
|
|
elseif self.talking==1 and rnd==5 and math.random(1,20)==1 then
|
|
aliveai.stand(self)
|
|
local rndpos
|
|
local obb
|
|
local d=99
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), self.distance/2)) do
|
|
if ob and ob:get_pos() and aliveai.visiable(self,ob) then
|
|
if d>aliveai.distance(self,ob:get_pos()) and not aliveai.same_bot(self,ob) then
|
|
rndpos=ob:get_pos()
|
|
obb=ob
|
|
end
|
|
if math.random(1,3)==1 then break end
|
|
end
|
|
end
|
|
if not obb then return end
|
|
aliveai.lookat(self,rndpos)
|
|
aliveai.rnd_talk_to(self,obb)
|
|
elseif rnd==6 and not self.folow then
|
|
local ob2
|
|
for _, ob in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), self.distance)) do
|
|
if aliveai.visiable(self,ob:get_pos()) and aliveai.viewfield(self,ob) and aliveai.get_bot_name(ob)~=self.botname then
|
|
ob2=ob
|
|
if math.random(1,2)==1 then
|
|
|
|
self.folow=ob
|
|
return self
|
|
end
|
|
end
|
|
end
|
|
self.folow=ob2
|
|
end
|
|
end
|
|
|
|
aliveai.punchdmg=function(ob,hp)
|
|
if not ob or type(ob)~="userdata" then return end
|
|
hp=hp or 1
|
|
ob:punch(ob,1,{full_punch_interval=1,damage_groups={fleshy=hp}})
|
|
end
|
|
|
|
aliveai.punch=function(self,ob,hp)
|
|
if not ob or type(ob)~="userdata" then return end
|
|
hp=hp or 1
|
|
if ob:get_luaentity() and ob:get_luaentity().itemstring then ob:remove() return end
|
|
|
|
ob:punch(self.object,1,{full_punch_interval=1,damage_groups={fleshy=hp}})
|
|
|
|
if self.object:get_hp()<=0 then
|
|
return nil
|
|
end
|
|
return self
|
|
end
|
|
|
|
|
|
aliveai.dmgbynode=function(self)
|
|
if self.damage_by_blocks~=1 then return self end
|
|
local pos=aliveai.newpos(self)
|
|
local d1=aliveai.def(pos:yy(0),"damage_per_second")
|
|
local d2=aliveai.def(pos:yy(-1),"damage_per_second")
|
|
if d1 and d1>0 then
|
|
aliveai.punchdmg(self.object,d1)
|
|
if not (self.dying or self.dead or self.sleeping) then
|
|
self.object:set_yaw(math.random(0,6.28))
|
|
aliveai.walk(self,2)
|
|
aliveai.showstatus(self,"hurts by node",1)
|
|
end
|
|
elseif d2 and d2>0 then
|
|
aliveai.punchdmg(self.object,d2)
|
|
if not (self.dying or self.dead or self.sleeping) then
|
|
self.object:set_yaw(math.random(0,6.28))
|
|
aliveai.walk(self,2)
|
|
aliveai.showstatus(self,"hurts by node",1)
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
|
|
aliveai.falling=function(self)
|
|
self.timerfalling=0
|
|
|
|
if self.floating==1 then
|
|
local a=self.object:get_acceleration()
|
|
local v=self.object:get_velocity()
|
|
if v.y~=0 and v.y<-0.02 or v.y>0.02 then
|
|
v.y=v.y*0.79
|
|
else
|
|
v.y=0
|
|
end
|
|
self.object:set_acceleration({x=0,y=0,z=0})
|
|
self.object:set_velocity({x=v.x,y=v.y,z=v.z})
|
|
return self
|
|
end
|
|
|
|
if aliveai.bots_delay2>aliveai.max_delay then return self end
|
|
if self.isrnd and self.path and self.in_liquid==nil then aliveai.path(self) return self end
|
|
if self.isrnd and self.done=="path" then self.timerfalling=0.2 self.done="" end
|
|
|
|
self.object:set_acceleration({x=0,y=-aliveai.gravity,z =0})
|
|
local pos=self.object:get_pos()
|
|
local node2=minetest.get_node(pos)
|
|
pos.y=pos.y-1
|
|
local node=minetest.get_node(pos)
|
|
-- if unknown
|
|
local test2=minetest.registered_nodes[node2.name]
|
|
local test=minetest.registered_nodes[node.name]
|
|
if not (test and test2) then return nil end
|
|
--water
|
|
if test2.liquid_viscosity>0 then
|
|
self.in_liquid=true
|
|
local s=1
|
|
if self.dying or self.dead or self.sleeping then s=-1 end
|
|
self.object:set_acceleration({x =0, y =0.1*s, z =0})
|
|
if self.object:get_velocity().y<-0.1 then
|
|
local y=self.object:get_velocity().y
|
|
self.object:set_velocity({x = self.move.x, y =y/2, z =self.move.z})
|
|
return self
|
|
end
|
|
self.object:set_velocity({x = self.move.x, y =1*s - (test2.liquid_viscosity*0.1), z =self.move.z})
|
|
if test.drowning and not self.drown then
|
|
self.drown=true
|
|
self.air=0
|
|
elseif self.air and self.air>=20 then
|
|
aliveai.punchdmg(self.object,1)
|
|
elseif self.air then
|
|
self.air=self.air+0.1
|
|
end
|
|
return self
|
|
-- ladder
|
|
elseif test.climbable then
|
|
self.in_liquid=true
|
|
if not self.air then
|
|
self.air=0
|
|
elseif self.air>=20 then
|
|
aliveai.punchdmg(self.object,1)
|
|
elseif self.air>=5 then
|
|
self.object:set_yaw(math.random(0,6.28))
|
|
aliveai.walk(self)
|
|
self.object:set_acceleration({x =0, y =-0.2, z =0})
|
|
self.object:set_velocity({x = self.move.x, y =-2, z =self.move.z})
|
|
return self
|
|
end
|
|
if self.climb==pos.y then self.air=self.air+0.1 end
|
|
self.climb=pos.y
|
|
self.object:set_acceleration({x =0, y =0.1, z =0})
|
|
if self.object:get_velocity().y<-0.1 then
|
|
local y=self.object:get_velocity().y
|
|
self.object:set_velocity({x = self.move.x, y =y/2, z =self.move.z})
|
|
return self
|
|
end
|
|
self.object:set_velocity({x = self.move.x, y =1, z =self.move.z})
|
|
aliveai.stand(self)
|
|
return self
|
|
-- no water or ledder
|
|
elseif self.in_liquid then
|
|
node=minetest.get_node({x=pos.x,y=pos.y-0.2,z=pos.z})
|
|
local n=minetest.registered_nodes[node.name]
|
|
self.air=nil
|
|
self.climb=nil
|
|
if n.liquid_viscosity>0 or n.climbable then
|
|
self.object:set_acceleration({x=0,y=0,z =0})
|
|
self.object:set_velocity({x = self.move.x, y =0, z =self.move.z})
|
|
return self
|
|
end
|
|
self.in_liquid=nil
|
|
self.object:set_acceleration({x=0,y=-10,z =0})
|
|
elseif test2.drowning>0 and self.drowning then
|
|
if not self.air then
|
|
self.drown=true
|
|
self.air=0
|
|
elseif self.air>=20 then
|
|
aliveai.punchdmg(self.object,1)
|
|
end
|
|
self.air=self.air+(test.drowning*0.1)
|
|
return self
|
|
elseif self.drown then
|
|
self.in_liquid=nil
|
|
self.air=nil
|
|
self.drown=nil
|
|
-- falling
|
|
elseif self.object:get_velocity().y~=0 then
|
|
if not self.fallingfrom or self.fallingfrom<pos.y then self.fallingfrom=pos.y end
|
|
end
|
|
--and hit the ground
|
|
if self.fallingfrom then
|
|
if self.object:get_velocity().y==0 then
|
|
local from=math.floor(self.fallingfrom+0.5)
|
|
local hit=math.floor(pos.y+0.5)
|
|
self.isfalling=nil
|
|
local d=from-hit
|
|
self.fallingfrom=nil
|
|
if d>=self.avoidy then
|
|
aliveai.punchdmg(self.object,d)
|
|
end
|
|
else
|
|
if not self.isfalling then
|
|
local from=math.floor(self.fallingfrom+0.5)
|
|
local hit=math.floor(pos.y+0.5)
|
|
local d=from-hit
|
|
if d>=self.avoidy then
|
|
self.isfalling=1
|
|
aliveai.sayrnd(self,"AHHH","",true)
|
|
end
|
|
end
|
|
end
|
|
elseif not self.told_flying and self.object:get_attach() and self.object:get_velocity().y==0 then
|
|
local n=minetest.registered_nodes[minetest.get_node({x=pos.x,y=pos.y-2,z=pos.z}).name]
|
|
if n and not (n.walkable or test.climbable or test2.liquid_viscosity>0 or n.liquid_viscosity>0) then
|
|
aliveai.sayrnd(self,"Hey, im flying!","",true)
|
|
self.told_flying=1
|
|
end
|
|
elseif self.told_flying and not self.object:get_attach() then
|
|
self.told_flying=nil
|
|
end
|
|
|
|
-- return if not moving
|
|
if self.move.x+self.move.z==0 then return self end
|
|
--disable rnd directions
|
|
if not self.path then
|
|
local j={}
|
|
local dmg=false
|
|
for i=1,self.avoidy*-1,-1 do
|
|
local nnode=minetest.registered_nodes[minetest.get_node({x=pos.x+self.move.x,y=pos.y+i,z=pos.z+self.move.z}).name]
|
|
if not nnode then return end
|
|
if nnode.damage_per_second>0 then dmg=true break end
|
|
if i<0 then j[2+(i*-1)]=nnode.walkable end
|
|
if nnode.walkable then break end
|
|
end
|
|
|
|
if self.fight and aliveai.distance(self,self.fight)<=self.arm*1.5 then
|
|
j[self.avoidy+2]=nil
|
|
dmg=nil
|
|
end
|
|
|
|
|
|
if j[self.avoidy+2]==false or dmg then
|
|
self.falllook={ignore=false}
|
|
local canuse={4.71,1.57,0,3.14}
|
|
local f={{x=1,z=0},{x=-1,z=0},{x=0,z=1},{x=0,z=-1}}
|
|
local use=1
|
|
j={}
|
|
for i=1,4,1 do
|
|
for i2=1,self.avoidy*-1,-1 do
|
|
local nnode=minetest.registered_nodes[minetest.get_node({x=pos.x+f[i].x,y=pos.y+i2,z=pos.z+f[i].z}).name]
|
|
if not nnode then return end
|
|
if nnode.damage_per_second>0 then
|
|
break
|
|
end
|
|
|
|
if nnode.walkable then
|
|
self.falllook[use]=canuse[i]
|
|
self.falllook.n=use
|
|
use=use+1
|
|
aliveai.showpath({x=pos.x+f[i].x,y=pos.y+i2+1,z=pos.z+f[i].z},2)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if use==1 then
|
|
self.falllook.ignore=true
|
|
self.falllook.times=5
|
|
else
|
|
self.falllook.times=10
|
|
local p=aliveai.creatpath(self,pos,aliveai.roundpos(pos))
|
|
if p~=nil then
|
|
self.path=p
|
|
self.timerfalling=0.1
|
|
aliveai.falling(self)
|
|
return self
|
|
end
|
|
end
|
|
self.falllook.times=10
|
|
return self
|
|
elseif j[4]==false and self.object:get_velocity().y==0 then
|
|
self.object:set_velocity({x = self.move.x*2, y = 5.2, z =self.move.z*2})
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
aliveai.jump=function(self,v)
|
|
if self.object:get_velocity().y==0 then
|
|
v=v or {}
|
|
v.y=v.y or 5.2
|
|
v.x=v.x or self.move.x
|
|
v.z=v.z or self.move.z
|
|
self.object:set_velocity({x = self.move.x, y = v.y, z =self.move.z})
|
|
end
|
|
end
|
|
|
|
aliveai.jumping=function(self)
|
|
local pos=self.object:get_pos()
|
|
if not pos then
|
|
return
|
|
end
|
|
|
|
pos.y=pos.y-self.basey
|
|
if minetest.get_node(pos)==nil then return end
|
|
local test=minetest.registered_nodes[minetest.get_node(pos).name]
|
|
-- jump inside block
|
|
if self.object:get_velocity().y==0 and test.walkable and test.drawtype~="nodebox" then
|
|
aliveai.jump(self)
|
|
aliveai.showstatus(self,"jump inside block")
|
|
if self.light==-1 then return self end
|
|
pos.y=pos.y+2
|
|
local n1=minetest.registered_nodes[minetest.get_node(pos).name]
|
|
if n1 and n1.walkable then
|
|
aliveai.stuckinblock(self)
|
|
end
|
|
return self
|
|
elseif test.walkable and test.drawtype=="nodeboax" and aliveai.group(pos,"aliveai_door") then
|
|
aliveai.open_door(self,pos)
|
|
return self
|
|
elseif aliveai.group({x=pos.x+self.move.x,y=pos.y,z=pos.z+self.move.z},"aliveai_door")>0 then
|
|
aliveai.open_door(self,{x=pos.x+self.move.x,y=pos.y,z=pos.z+self.move.z})
|
|
return self
|
|
end
|
|
|
|
if self.move.x+self.move.z~=0 and self.object:get_velocity().y==0 then
|
|
local x=self.move.x
|
|
local z=self.move.z
|
|
local j={}
|
|
for i=-2,3,1 do
|
|
local jnod=minetest.registered_nodes[minetest.get_node({x=pos.x+x,y=pos.y+i,z=pos.z+z}).name]
|
|
if not jnod then return end
|
|
j[i+3]=jnod.walkable
|
|
end
|
|
-- jump x1
|
|
if j[3] and j[4]==false and j[5]==false then
|
|
aliveai.jump(self)
|
|
minetest.after(0.5, function(self)
|
|
if self and self.object and self.object:get_luaentity() and self.object:get_luaentity().name then
|
|
self.object:set_velocity({x = self.move.x*2, y = self.object:get_velocity().y, z =self.move.z*2})
|
|
|
|
end
|
|
end, self)
|
|
aliveai.showstatus(self,"jump")
|
|
return self
|
|
--jump x2
|
|
elseif j[4] and j[5]==false and j[6]==false then
|
|
aliveai.jump(self,{y=7})
|
|
minetest.after(0.5, function(self)
|
|
if self and self.object and self.object:get_luaentity() and self.object:get_luaentity().name then
|
|
self.object:set_velocity({x = self.move.x*2, y = self.object:get_velocity().y, z =self.move.z*2})
|
|
end
|
|
end, self)
|
|
aliveai.showstatus(self,"jump x2")
|
|
return self
|
|
--wall or door
|
|
elseif not self.path and j[4] and j[6] then
|
|
aliveai.showstatus(self,"wall")
|
|
aliveai.stand(self)
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
aliveai.neartarget=function(self,p,starty,endy,stepy)
|
|
aliveai.showstatus(self,"check neartarget")
|
|
local a={
|
|
{x=p.x-1,z=p.z},
|
|
{x=p.x+1,z=p.z},
|
|
{x=p.x,z=p.z+1},
|
|
{x=p.x,z=p.z-1},
|
|
{x=p.x+1,z=p.z+1},
|
|
{x=p.x-1,z=p.z-1},
|
|
{x=p.x-1,z=p.z+1},
|
|
{x=p.x+1,z=p.z-1}}
|
|
local n=8
|
|
local o=aliveai.roundpos(self.object:get_pos())
|
|
local last_p=nil
|
|
local last_able=nil
|
|
starty=starty or 1
|
|
endy=endy or -4
|
|
stepy=stepy or -1
|
|
for y=starty,endy,stepy do
|
|
for i=1,8,1 do
|
|
last_p={x=a[i].x,y=p.y+y,z=a[i].z}
|
|
if minetest.registered_nodes[minetest.get_node(last_p).name] and minetest.registered_nodes[minetest.get_node(last_p).name].walkable==false then
|
|
local nod1=minetest.registered_nodes[minetest.get_node({x=a[i].x,y=p.y+y-1,z=a[i].z}).name]
|
|
local nod2=minetest.registered_nodes[minetest.get_node({x=a[i].x,y=p.y+y+1,z=a[i].z}).name]
|
|
if nod1 and nod1.walkable and
|
|
nod2 and nod2.walkable==false then
|
|
last_able={x=a[i].x,y=p.y+y,z=a[i].z}
|
|
if o.y==last_able.y then
|
|
aliveai.showpath(last_able,3)
|
|
return last_able
|
|
end
|
|
end
|
|
else
|
|
n=n-1
|
|
end
|
|
end
|
|
if n==0 and y<=0 then
|
|
break
|
|
end
|
|
n=8
|
|
end
|
|
if last_able~=nil then
|
|
aliveai.showpath(last_able,3)
|
|
return last_able
|
|
end
|
|
return nil
|
|
end
|
|
|
|
aliveai.findnode=function(self,node_name,ignores)
|
|
aliveai.showstatus(self,"find node")
|
|
local pos=self.object:get_pos()
|
|
pos.y=pos.y-1
|
|
local re={pos={},path={}}
|
|
local np=minetest.find_node_near(pos, self.distance,{node_name})
|
|
if ignores and np~=nil then -- ignore unable nodes
|
|
for _, s in pairs(ignores) do
|
|
if aliveai.samepos(np,s) then
|
|
return ignores
|
|
end
|
|
end
|
|
end
|
|
if np~=nil and minetest.is_protected(np,"")==false then
|
|
re.pos=np
|
|
local np2=nil
|
|
local near=aliveai.neartarget(self,np)
|
|
if near~=nil then
|
|
np2=near
|
|
else
|
|
np2=minetest.find_node_near(np, 2,{"air"})
|
|
end
|
|
if np2~=nil then
|
|
local np3=np
|
|
if minetest.registered_nodes[minetest.get_node({x=np2.x,y=np2.y-1,z=np2.z}).name].walkable==false
|
|
or minetest.registered_nodes[minetest.get_node({x=np2.x,y=np2.y+1,z=np2.z}).name].walkable==false then
|
|
np3=np2
|
|
aliveai.showpath(np,3)
|
|
end
|
|
local pos1=aliveai.roundpos(pos)
|
|
local p=aliveai.creatpath(self,pos1,np3)
|
|
if p~=nil then
|
|
re.path=p
|
|
return re
|
|
else
|
|
if not ignores then ignores={} end -- ignore unable nodes
|
|
table.insert(ignores, np)
|
|
return ignores
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
aliveai.ignorenode=function(self,pos)
|
|
if self.mine then
|
|
if not self.mine.ignore then self.mine.ignore={} end -- ignore unable nodes
|
|
table.insert(self.mine.ignore, pos)
|
|
return self
|
|
end
|
|
end
|
|
|
|
aliveai.samepos=function(pos1,pos2)
|
|
return (pos1 and pos2 and pos1.x==pos2.x and pos1.y==pos2.y and pos1.z==pos2.z)
|
|
end
|
|
|
|
aliveai.roundpos=function(pos)
|
|
if pos and pos.x and pos.y and pos.z then
|
|
pos.x = math.floor(pos.x+0.5)
|
|
pos.y = math.floor(pos.y+0.5)
|
|
pos.z = math.floor(pos.z+0.5)
|
|
return pos
|
|
end
|
|
return nil
|
|
end
|
|
|
|
aliveai.viewfield=function(self,ob)
|
|
if not (self and self.object and ob) then return false end
|
|
local pos1=self.object:get_pos()
|
|
local pos2 = type(ob) == "userdata" and ob:get_pos() or ob
|
|
return aliveai.distance(pos1,pos2)>aliveai.distance(aliveai.pointat(self,0.1),pos2)
|
|
end
|
|
|
|
aliveai.pointat=function(self,d)
|
|
local pos=self.object:get_pos()
|
|
local yaw=aliveai.nan(self.object:get_yaw())
|
|
d=d or 1
|
|
local x =math.sin(yaw) * -d
|
|
local z =math.cos(yaw) * d
|
|
return {x=pos.x+x,y=pos.y,z=pos.z+z}
|
|
end
|
|
|
|
aliveai.distance=function(pos1,pos2)
|
|
if not (pos1 and pos1.x and pos2 and pos2.x) then
|
|
return 0
|
|
end
|
|
pos1 = type(pos1) == "userdata" and pos1:get_pos() or pos1.object and pos1.object:get_pos() or pos1
|
|
pos2 = type(pos2) == "userdata" and pos2:get_pos() or pos2.object and pos2.object:get_pos() or pos2
|
|
if type(pos2) ~= "table" then
|
|
return 0
|
|
end
|
|
return vector.distance(pos1,pos2)
|
|
end
|
|
|
|
aliveai.visiable=function(pos1,pos2)
|
|
pos1 = type(pos1) == "userdata" and pos1:get_pos() or pos1.object and pos1.object:get_pos() or pos1
|
|
pos2 = type(pos2) == "userdata" and pos2:get_pos() or pos2
|
|
|
|
if not (pos1 and pos1.x and pos2 and pos2.x) then
|
|
return false
|
|
end
|
|
|
|
local v = {x = pos1.x - pos2.x, y = pos1.y - pos2.y-1, z = pos1.z - pos2.z}
|
|
v.y=v.y-1
|
|
local amount = (v.x ^ 2 + v.y ^ 2 + v.z ^ 2) ^ 0.5
|
|
local d=vector.distance(pos1,pos2)
|
|
v.x = (v.x / amount)*-1
|
|
v.y = (v.y / amount)*-1
|
|
v.z = (v.z / amount)*-1
|
|
for i=1,d,1 do
|
|
local node = minetest.registered_nodes[minetest.get_node({x=pos1.x+(v.x*i),y=pos1.y+(v.y*i),z=pos1.z+(v.z*i)}).name]
|
|
if node and node.walkable then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
aliveai.walk=function(self,sp)
|
|
local pos=self.object:get_pos()
|
|
local yaw=aliveai.nan(self.object:get_yaw())
|
|
sp=sp or 1
|
|
local x =math.sin(yaw) * -1
|
|
local z =math.cos(yaw) * 1
|
|
local y=self.object:get_velocity().y
|
|
local s=(self.move.speed+1)*sp
|
|
self.move.x=x*sp
|
|
self.move.z=z*sp
|
|
if self.floating==1 and self.lookat then
|
|
if pos.y<self.lookat.y-0.25 then
|
|
y=s
|
|
elseif pos.y>self.lookat.y+0.25 then
|
|
y=-s
|
|
end
|
|
self.lookat=nil
|
|
end
|
|
self.object:set_velocity({
|
|
x = x*s,
|
|
y = y,
|
|
z = z*s})
|
|
if self.hugwalk==0 then
|
|
aliveai.anim(self,"walk")
|
|
else
|
|
aliveai.anim(self,"hugwalk")
|
|
end
|
|
return self
|
|
end
|
|
|
|
aliveai.stand=function(self)
|
|
if not self.move or not self.object or not self.object:get_velocity() then aliveai.kill(self) return end
|
|
self.move.x=0
|
|
self.move.z=0
|
|
self.object:set_velocity({
|
|
x = 0,
|
|
y = self.object:get_velocity().y,
|
|
z = 0})
|
|
aliveai.anim(self,"stand")
|
|
return self
|
|
end
|
|
|
|
aliveai.lookat=function(self,pos2)
|
|
if type(pos2)=="table" then
|
|
local pos1=self.object:get_pos()
|
|
local vec = {x=pos1.x-pos2.x, y=pos1.y-pos2.y, z=pos1.z-pos2.z}
|
|
local yaw = aliveai.nan(math.atan(vec.z/vec.x)-math.pi/2)
|
|
if pos1.x >= pos2.x then yaw = yaw+math.pi end
|
|
self.object:set_yaw(yaw)
|
|
self.lookat=pos2
|
|
elseif type(pos2)=="number" then
|
|
self.object:set_yaw(pos2)
|
|
end
|
|
return self
|
|
end
|
|
|
|
aliveai.anim=function(self,type)
|
|
if self.visual~="mesh" then return end
|
|
if type==self.anim or self.anim==nil then return self end
|
|
local a=self.animation[type]
|
|
if not a then return self end
|
|
self.object:set_animation({ x=a.x, y=a.y, },a.speed,a.loop)
|
|
self.anim=type
|
|
return self
|
|
end
|
|
|
|
aliveai.strpos=function(str,spl)
|
|
if str==nil then return "" end
|
|
if spl then
|
|
local c=","
|
|
if string.find(str," ") then c=" " end
|
|
local s=str.split(str,c)
|
|
if s[3]==nil then
|
|
return nil
|
|
else
|
|
return {x=tonumber(s[1]),y=tonumber(s[2]),z=tonumber(s[3])}
|
|
end
|
|
else if str.x and str.y and str.z then
|
|
str=aliveai.roundpos(str)
|
|
return str.x .."," .. str.y .."," .. str.z
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
end
|
|
|
|
aliveai.genname=function(self)
|
|
local r=math.random(3,15)
|
|
local s=""
|
|
local rnd
|
|
for i=1, r do
|
|
rnd=math.random(1, 4)
|
|
if rnd==1 then
|
|
s=s .. string.char(math.random(97, 122))
|
|
elseif rnd==2 then
|
|
s=s .. string.char(math.random(48, 57))
|
|
else
|
|
s=s .. string.char(math.random(65, 90))
|
|
end
|
|
end
|
|
return s
|
|
end
|
|
|
|
aliveai.max=function(self,update)
|
|
local c=0
|
|
for i,v in pairs(aliveai.active) do
|
|
if aliveai.gethp(v,1)<0 then
|
|
table.remove(aliveai.active,c)
|
|
else
|
|
c=c+1
|
|
end
|
|
end
|
|
aliveai.active_num=c
|
|
local new=aliveai.newbot
|
|
aliveai.newbot=nil
|
|
if new and aliveai.active_num>aliveai.max_new_bots then
|
|
self.object:remove()
|
|
return self
|
|
end
|
|
if update then
|
|
return self
|
|
elseif aliveai.bots_delay2>aliveai.max_delay and (self.old==0 or self.delay_average.time>1) then
|
|
if self.old==0 then
|
|
print("aliveai: removed","new","active bots: " ..aliveai.active_num)
|
|
else
|
|
print("aliveai: removed","delay: " ..self.delay_average.time,"active bots: " ..aliveai.active_num)
|
|
end
|
|
aliveai.bots_delay2= aliveai.bots_delay2*0.99
|
|
|
|
aliveai.sayrnd(self,"LAAAAAAAAG!")
|
|
|
|
self.object:remove()
|
|
return self
|
|
end
|
|
if not aliveai.active[self.botname] then
|
|
aliveai.active[self.botname]=self.object
|
|
end
|
|
end
|
|
|
|
aliveai.botdelay=function(self,a)
|
|
if self.delaytimeout then
|
|
if os.clock()>self.delaytimeout then
|
|
self.delaytimeout=nil
|
|
a=1
|
|
else
|
|
return self
|
|
end
|
|
elseif not self.delay_average then
|
|
self.delay_average={time=0}
|
|
end
|
|
|
|
local new=os.clock()-self.delaytimer
|
|
|
|
if aliveai.game_paused then
|
|
self.delay_average={time=0}
|
|
self.delaytimer=os.clock()
|
|
return
|
|
elseif not a then
|
|
self.delaytimer=os.clock()
|
|
return
|
|
else
|
|
if not self.delay_steps_to then self.delay_steps_to=0 end
|
|
self.delay_steps_to=self.delay_steps_to+1
|
|
if self.delay_steps_to<10 then return end
|
|
self.delay_steps_to=0
|
|
|
|
local p=math.floor(new/aliveai.delaytime)/10
|
|
|
|
if #self.delay_average>10 then
|
|
table.remove(self.delay_average,1)
|
|
local d=self.delay_average
|
|
self.delay_average.time=aliveai.nan((d[1]+d[2]+d[3]+d[4]+d[5]+d[6]+d[7]+d[8]+d[9]+d[10])/10)
|
|
|
|
else
|
|
table.insert(self.delay_average,p)
|
|
end
|
|
|
|
if self.terminal_user then
|
|
if aliveai.terminal_users[self.terminal_user] and aliveai.terminal_users[self.terminal_user].botname==self.botname then
|
|
if aliveai.terminal_users[self.terminal_user].status then
|
|
aliveai.show_terminal(minetest.get_player_by_name(self.terminal_user),1)
|
|
end
|
|
else
|
|
self.terminal_user=nil
|
|
end
|
|
end
|
|
if self.delay_average.time>1 then
|
|
self.delaytimeout=os.clock()+(p-1)
|
|
if self.type~="npc" or (self.type=="npc" and self.delay_average.time>1.5) then
|
|
aliveai.max(self)
|
|
end
|
|
if aliveai.status==true then
|
|
aliveai.showstatus(self,(self.delay_average.time*100) .."% delay",1)
|
|
end
|
|
elseif aliveai.status==true and self.delay_average.time>0.5 then
|
|
aliveai.showstatus(self,(self.delay_average.time*100) .."% delay",4)
|
|
end
|
|
|
|
self.delaytimer=os.clock()
|
|
end
|
|
end
|
|
|