aliveai/aliveai/bot.lua

783 lines
23 KiB
Lua

aliveai.main=function(self, dtime)
if aliveai.systemfreeze==1 then
if self.floating==0 then
self.object:set_acceleration({x=0,y=0,z =0})
end
self.object:set_velocity({x=0,y=0,z=0})
return self
end
if aliveai.bots_delay2>aliveai.max_delay then
if self.old==0 or (self.old==1 and aliveai.bots_delay2>aliveai.max_delay*1.2) then aliveai.max(self) end
return self
end
if aliveai.botdelay(self,1) then return self end
aliveai.bot(self, dtime)
aliveai.botdelay(self)
end
aliveai.bot=function(self, dtime)
aliveai.bots_delay=aliveai.bots_delay+dtime
self.timer=self.timer+dtime
self.timerfalling=self.timerfalling+dtime
if self.timerfalling>0.2 then aliveai.falling(self) end
if self.timer<=self.time then return self end
self.timer=0
--betweens
if aliveai.dying(self) then return self end
if not aliveai.dmgbynode(self) then return self end
if self.step(self,dtime) or self.controlled==1 then return self end
if aliveai.sleep(self) then return self end
aliveai.jumping(self)-- if need to jump
if aliveai.fight(self) then return self end
if aliveai.fly(self) then return self end
if aliveai.come(self) then return self end
if aliveai.folowing(self) then return self end
aliveai.searchobjects(self)
if aliveai.need_helper(self) then return self end -- give stuff
if aliveai.light(self) then return self end
if aliveai.node_handler(self) then return self end
if aliveai.timer(self) then return self end -- remove monsters
if aliveai.rndgoal(self) then return self end
aliveai.msghandler(self)
aliveai.pickup(self)-- if can pick up items
if aliveai.lookaround(self) then return self end
--betweens helpers
if self.isrnd and self.pickupgoto then return self end
--events
if self.mine then
aliveai.mine(self)
return self
end
if self.findspace then
aliveai.findspace(self)
return self
end
if self.build then
aliveai.build(self)
return self
end
--tasks
if self.task=="build" then
aliveai.task_build(self)
return self
end
--task create
if self.task1(self) then return self end
if self.task2(self) then return self end
if self.task3(self) then return self end
if self.task4(self) then return self end
if self.task5(self) then return self end
if self.task=="" then
aliveai.rndwalk(self)
end
return self
end
aliveai.do_nothing=function(self)
return
end
aliveai.create_bot=function(def)
if not def then def={} end
def.name=def.name or "bot"
def.mod_name=minetest.get_current_modname()
def.spawn_y=def.spawn_y or 1
if aliveai.smartshop and def.on_step==nil then def.on_step=aliveai.use_smartshop end
if not def.click then def.click=aliveai.give_to_bot end
def.texture=def.texture or "character.png"
if def.type and def.dropbones~=1 then
def.dropbones=0
end
local itemtexture=def.texture
if type(def.texture)=="table" and type(def.texture[1])=="string" then itemtexture=def.texture[1] end
if aliveai.armor_3d and not def.visual and not def.texture[2] then
def.basey = def.basey or -0.3
def.collisionbox = def.collisionbox or {-0.35,0,-0.35,0.35,1.8,0.35}
if def.type and def.type~="npc" then
def.usearmor=0
else
def.texture={def.texture,"aliveai_air.png","aliveai_air.png",def.texture,}
def.mesh=aliveai.armor_3d
def.animation={}
def.animation["stand"]={x=0, y=79,speed=30,loop=0}
def.animation["lay"]={x=162,y=166,speed=30,loop=0}
def.animation["walk"]={x=168, y=187, speed=30,loop=0}
def.animation["mine"]={x=189, y=198, speed=30,loop=0}
def.animation["walk_mine"]={x=200, y=219, speed=30,loop=0}
def.animation["sit"]={x=81, y=160, speed=30,loop=0}
end
elseif type(def.texture)~="table" then
def.texture={def.texture}
end
if def.animation and (def.visual==nil or def.visual=="mesh") then
for i, v in pairs(def.animation) do
if i=="attack" or i=="fight" then i="mine" end
def.animation[i].x=def.animation[i].x or 0
def.animation[i].y=def.animation[i].y or 0
def.animation[i].speed=def.animation[i].speed or 0
def.animation[i].loop=def.animation[i].loop or 0
end
end
aliveai.registered_bots[def.mod_name ..":" .. def.name]={
dmg=def.dmg or 1,
hp=def.hp or 20,
team=def.team or "Sam",
description=def.description or "No description able",
name=def.name,
mod_name=def.mod_name,
bot=def.mod_name ..":" .. def.name,
type=def.type or "npc",
item=def.mod_name ..":" .. def.name .. "_spawner",
textures=def.texture,
spawn_y=def.spawn_y,
team=def.team or aliveai.default_team,
start_with_items=def.start_with_items or "",
floating=def.floating or 0,
attacking=def.attacking or 0,
light=def.light or 1,
mindamage=def.mindamage or 0,
}
if not def.no_spawnitem and (not def.visual or def.visual=="mesh") then
if def.texture~=nil and type(def.texture)=="string" then def.texture={def.texture} end
minetest.register_node(def.mod_name ..":" .. def.name .."_spawner", {
description = def.name .." spawner",
wield_scale={x=0.2,y=0.2,z=0.2},
tiles=def.texture,
drawtype="mesh",
mesh=def.mesh or aliveai.character_preview_model,
paramtype="light",
visual_scale=0.1,
on_place = function(itemstack, user, pointed_thing)
if pointed_thing.type=="node" then
local pos=aliveai.roundpos(pointed_thing.above)
pos.y=pos.y+0.5 + def.spawn_y
minetest.add_entity(pos, def.mod_name ..":" .. def.name):set_yaw(math.random(0,6.28))
itemstack:take_item()
minetest.check_for_falling(pos)
end
return itemstack
end,
})
elseif not def.no_spawnitem then
minetest.register_craftitem(def.mod_name ..":" .. def.name .."_spawner", {
description = def.name .." spawner",
inventory_image = itemtexture or "character.png",
on_place = function(itemstack, user, pointed_thing)
if pointed_thing.type=="node" then
local pos=aliveai.roundpos(pointed_thing.above)
pos.y=pos.y+0.5 + def.spawn_y
minetest.add_entity(pos, def.mod_name ..":" .. def.name):set_yaw(math.random(0,6.28))
itemstack:take_item()
end
return itemstack
end,
})
elseif def.no_spawnitem then
if not minetest.registered_items[def.no_spawnitem] then
def.no_spawnitem="aliveai:ai_fake_item_spawner"
end
aliveai.registered_bots[def.mod_name ..":" .. def.name].item=def.no_spawnitem
end
if not def.no_entity then
def.drop_dead_body=def.drop_dead_body or 1
if def.texture~=nil and type(def.texture)=="string" then def.texture={def.texture} end
minetest.register_entity(def.mod_name ..":" .. def.name,{
hp_max = def.hp or 20,
physical = true,
weight = 5,
collisionbox = def.collisionbox or {-0.35,-1.0,-0.35,0.35,0.8,0.35}, -- new box {-0.35,0,-0.35,0.35,1.8,0.35}
visual = def.visual or "mesh",
visual_size = def.visual_size or {x=1,y=1},
mesh = def.mesh or aliveai.character_model,
textures = def.texture,
colors = {},
spritediv = {x=1, y=1},
initial_sprite_basepos = {x=0, y=0},
is_visible = true,
makes_footstep_sound = true,
on_rightclick=function(self, clicker,name)
self.click(self,clicker)
end,
on_punch=function(self, puncher, time_from_last_punch, tool_capabilities, dir)
local pos=self.object:get_pos()
if self.destroy or minetest.get_node({x=pos.x,y=pos.y-2,z=pos.z}).name=="ignore" then
self.object:remove()
aliveai.max(self,true)
return self
end
tool_capabilities.damage_groups.fleshy=tool_capabilities.damage_groups.fleshy or 1
local mindmg=tool_capabilities.damage_groups.fleshy>=self.mindamage
local dmg=0
if tool_capabilities and tool_capabilities.damage_groups and tool_capabilities.damage_groups.fleshy then
if not self.hp then self.hp=0 end
if mindmg==true then
self.hp=self.hp-tool_capabilities.damage_groups.fleshy
self.object:set_hp(self.hp)
dmg=tool_capabilities.damage_groups.fleshy
self.mood=self.mood-2
end
end
if dir~=nil and mindmg==true then
local v={x = dir.x*3,y = self.object:get_velocity().y,z = dir.z*3}
self.object:set_velocity(v)
local r=math.random(1,99)
self.sleeping=nil
self.onpunch_r=r
minetest.after(1, function(self,v,r)
if self and self.object and self.hp>0 and self.onpunch_r==r and aliveai.samepos(aliveai.roundpos(self.object:get_velocity()),aliveai.roundpos(v)) then
self.object:set_velocity({x = 0,y = self.object:get_velocity().y,z = 0})
end
end, self,v,r)
end
if mindmg==true then
aliveai.showhp(self)
end
-- death
if self.dying then
if self.hp<=0 then
aliveai.dying(self,2)
else
return self
end
end
if self.object:get_hp()<=0 and not (self.dead or self.dying) then
local pos=self.object:get_pos()
if self.drop_dead_body==1 then
aliveai.showstatus(self,"drop dead body")
aliveai.stand(self)
aliveai.dying(self,1)
end
self.death(self,puncher,pos)
aliveai.invdropall(self)
aliveai.max(self,true)
return self
end
self.punched(self,puncher,dmg)
if aliveai.armor and self.armor then aliveai.armor(self,{dmg=true}) end
if self.path then
aliveai.exitpath(self)
end
minetest.after(2, function(self)
aliveai.eat(self)
end,self)
if not aliveai.same_bot(self,puncher) then
local known=aliveai.getknown(self,puncher)
if known=="member" then
aliveai.known(self,puncher,"")
if math.random(1,3)==1 then aliveai.say(self,"I dont like you anymore") end
return self
elseif self.escape==1 and known=="fly" or self.fighting~=1 then
if self.temper>-5 then
self.temper=self.temper-0.3
end
if math.random(1,3)==1 then aliveai.sayrnd(self,"ahh") end
self.fly=puncher
aliveai.known(self,puncher,"fly")
elseif self.fighting==1 then
if self.temper<5 then
self.temper=self.temper+1
end
self.fight=puncher
aliveai.known(self,puncher,"fight")
if math.random(1,3)==1 then aliveai.sayrnd(self,"ouch") end
end
aliveai.lookat(self,puncher:get_pos())
return self
end
if math.random(1,3)==1 then aliveai.sayrnd(self,"ouch") end
return self
end,
on_activate=function(self, staticdata)
if staticdata=="destroy_on_load" then self.object:remove() return end
self.self_definitions(self)
if staticdata~="" then
local r=aliveai.convertdata(staticdata)
if not r.old then
r=minetest.deserialize(staticdata)
end
self.inv={}
self.ignore_item={}
self.ignore_nodes={}
self.known={}
self.old=r.old
self.mood=r.mood
self.team=r.team
self.namecolor=r.namecolor
self.known=r.known
self.inv=r.inv
self.ignore_item=r.ignore_item
self.ignore_nodes=r.ignore_ingnore_nodes
self.task=r.task
self.taskstep=r.taskstep
self.botname=r.botname
self.start_with_items=""
self.dmg=r.dmg
self.floating=r.floating
self.sleeping=r.sleeping
self.attention_nodes=r.attention_nodes
if r.savetool then self.tools=r.tools self.savetool=1 self.tool_near=1 end
if r.home then self.home=r.home end
if r.resources then self.resources=r.resources end
for i, v in pairs(r) do
--local name=i
-- if string.find(name,"storge")==1 then
--name=string.gsub(name,"storge", "save__")
--end
if string.find(i,"save__")==1 then
self[i]=v
end
end
if r.hp then self.object:set_hp(r.hp) end
if r.dying then
self.dying=r.dying
minetest.after(0.1, function(self)
aliveai.anim(self,"lay")
end, self)
end
for i, s in pairs(aliveai.loaddata) do
s(self,r)
end
aliveai.showstatus(self,"loaded")
end
if self.attention_nodes==nil then self.attention_nodes={} end
if self.inv==nil then self.inv={} end
if self.ignore_item==nil then self.ignore_item={} end
self.move={x=0,y=0,z=0,speed=1}
aliveai.anim(self,"stand")
aliveai.floating(self,self.floating)
if self.botname=="" then self.botname=aliveai.genname() end
if self.namecolor~="" then self.object:set_properties({nametag=self.botname,nametag_color="#" .. self.namecolor}) end
if self.start_with_items~="" and type(self.start_with_items)=="table" then
for i, s in pairs(self.start_with_items) do
aliveai.invadd(self,i,s,true)
end
self.start_with_items=""
end
self.delay_average={time=0}
aliveai.max(self)
if self.old~=1 then
if not self.object:get_pos() then
self.object:remove()
return
end
self.spawn(self)
aliveai.showstatus(self,"new bot spawned")
if self.type=="npc" and math.random(1, aliveai.get_random_stuff_chance)==1 then
local itmnum=0
for i, v in pairs(minetest.registered_items) do
if math.random(1,20)==1 then
if minetest.get_all_craft_recipes(i) and i~="air" then
self.inv[i]=1
itmnum=itmnum+1
if itmnum>9 then break end
end
end
end
end
else
self.on_load(self)
aliveai.showstatus(self,"bot loaded")
end
self.lastitem_name="wooden planks"
self.talking_to="you"
self.lastitem_count=1
self.hp=self.object:get_hp()
if aliveai.creative=="true" then
self.creative=1
self.superbuild=1
end
return self
end,
get_staticdata = function(self)
aliveai.max(self)
if (self.isfalling and self.kill_deep_falling==1) or self.dead then
return "destroy_on_load"
end
local r={inv=self.inv,old=1,hp=self.object:get_hp(),
task=self.task,
taskstep=self.taskstep,
ignore_item=self.ignore_item,
known=self.known,
ignore_nodes=self.ignore_ingnore_nodes,
botname=self.botname,
dmg=self.dmg,
mood=self.mood,
team=self.team,
namecolor=self.namecolor,
floating=self.floating,
attention_nodes=self.attention_nodes,
}
if self.sleeping then r.sleeping=self.sleeping end
if self.home then r.home=self.home end
if self.resources then r.resources=self.resources end
if self.savetool then r.tools=self.tools r.savetool=1 end
if self.start_with_items then r.start_with_items="" end
if self.dying then r.dying=self.dying end
for i, v in pairs(self) do
if string.find(i,"storge")==1 or string.find(i,"save__")==1 then
r[i]=v
end
end
for i, s in pairs(aliveai.savedata) do
local rr=s(self,r)
if rr then
for i1, s2 in pairs(rr) do
r[i1]=s2
end
end
end
return minetest.serialize(r)
end,
on_step=aliveai.main,
botname=def.botname or "",
namecolor= def.name_color or "ffffff",
timerfalling= 0,
aliveai= true,
old= 0,
temper= 0,
mood=10,
rnd= 0,
isrnd= false,
done="",
taskstep= 0,
task= "",
pathn= 1,
anim= "",
timer= 0,
time= 1,
otime= 1,
timer3= 0,
delaytimer=0,
delaytimer2=0,
--define
arm= def.arm or 5,
dmg= def.dmg or 1,
dropbones= def.dropbones or 1,
superbuild= def.superbuild or 0,
house=def.house or "",
creative=def.creative or "",
leader=def.leader or 0,
team= def.team or aliveai.default_team,
visual= def.visual or "mesh",
basey= def.basey or 0.7, -- new -0.3
damage_by_blocks= def.damage_by_blocks or 1,
kill_deep_falling= def.kill_deep_falling or 1,
crafting= def.crafting or 1,
avoidy= def.avoid_height or 6,
drop_dead_body=def.drop_dead_body or 1,
type= def.type or "npc",
distance= def.distance or 15,
tools= def.tools or "",
tool_index=def.tool_index or 1,
tool_reuse=def.tool_reuse or 0,
tool_chance= def.tool_chance or 5,
tool_see= def.tool_see or 1,
tool_near= def.tool_near or 0,
escape= def.escape or 1,
fighting= def.fighting or 1,
attack_players= def.attack_players or 0,
attack_chance= def.attack_chance or 10,
smartfight= def.smartfight or 1,
usearmor=def.usearmor or 1,
building= def.building or 1,
pickuping= def.pickuping or 1,
attacking= def.attacking or 0,
coming= def.coming or 1,
work_helper= def.work_helper or 0,
coming_players= def.coming_players or 1,
talking= def.talking or 1,
stealing= def.stealing or 0,
steal_chance= def.steal_chance or 0,
start_with_items= def.start_with_items or "",
light= def.light or 1,
hugwalk= def.hugwalk or 0,
lowestlight= def.lowest_light or 10,
lightdamage=def.hurts_by_light or 1,
annoyed_by_staring= def.annoyed_by_staring or 1,
drowning= def.drowning or 1,
mindamage=def.mindamage or 0,
--animation
animation=def.animation or {
stand={x=1,y=39,speed=30},
walk={x=41,y=61,speed=30},
mine={x=65,y=75,speed=30},
hugwalk={x=80,y=99,speed=30},
lay={x=113,y=123,speed=0},
sit={x=101,y=111,speed=0},
},
--functions
on_chat= def.on_chat or aliveai.do_nothing,
on_spoken_to= def.on_spoken_to or aliveai.on_spoken_to,
on_fighting= def.on_fighting or aliveai.do_nothing,
on_escaping= def.on_escaping or aliveai.do_nothing,
on_punching= def.on_punching or aliveai.do_nothing,
on_punch_hit= def.on_punch_hit or aliveai.do_nothing,
on_detect_enemy= def.on_detect_enemy or aliveai.do_nothing,
on_detecting_enemy= def.on_detecting_enemy or aliveai.do_nothing,
death= def.death or aliveai.do_nothing,
spawn= def.spawn or aliveai.do_nothing,
on_load= def.on_load or aliveai.do_nothing,
on_random_walk= def.on_random_walk or aliveai.do_nothing,
click= def.click or aliveai.do_nothing,
punched= def.on_punched or aliveai.do_nothing,
on_meet= def.on_meet or aliveai.do_nothing,
step= def.on_step or aliveai.do_nothing,
on_dig= def.on_dig or aliveai.do_nothing,
on_blow=def.on_blow or aliveai.do_nothing,
--tasks
task1= def.task1 or aliveai.task_build,
task2= def.task2 or aliveai.task_stay_at_home,
task3= def.task3 or aliveai.task_farming,
task4= def.task4 or aliveai.do_nothing,
task5= def.task5 or aliveai.do_nothing,
floating= def.floating or 0,
self_definitions = function(self)
if def.self and type(def.self)=="table" then
for i, s in pairs(def.self) do
if not self[i] then
self[i]=s
end
end
end
end,
})
end
if not aliveai.spawning or def.no_spawning then
aliveai.loaded(def.mod_name ..":" .. def.name)
return
end
def.spawn_in= def.spawn_in or "air"
def.spawn_chance= def.spawn_chance or 1000
def.check_spawn_space= def.check_spawn_space or 1
if def.light==nil then def.light=1 end
if def.lowest_light==nil then def.lowest_light=10 end
minetest.register_abm({
nodenames = def.spawn_on or {"group:spreading_dirt_type","group:sand","default:snow"},
interval = def.spawn_interval or 30,
chance = def.spawn_chance,
action = function(pos)
if aliveai.systemfreeze==1 then
return
end
local pos1={x=pos.x,y=pos.y+1,z=pos.z}
local pos2={x=pos.x,y=pos.y+2,z=pos.z}
local l = minetest.get_node_light(pos1) or 0
if l==nil then return true end
if aliveai.random(1,def.spawn_chance)==1
and (def.light==0
or (def.light>0 and l>=def.lowest_light)
or (def.light<0 and l<=def.lowest_light)) then
if aliveai.check_spawn_space==false or def.check_spawn_space==0 or ((minetest.get_node(pos1).name==def.spawn_in and minetest.get_node(pos2).name==def.spawn_in) or minetest.get_item_group(minetest.get_node(pos1).name,def.spawn_in)>0) then
aliveai.newbot=true
pos1.y=pos1.y+def.spawn_y
local m = minetest.add_entity(pos1, def.mod_name ..":" .. def.name)
if m then
m:set_yaw(math.random(0,6.28))
end
end
end
end,
})
aliveai.loaded(def.mod_name ..":" .. def.name)
end
aliveai.loaded=function(name)
aliveai.loaded_objects=aliveai.loaded_objects+1
print("[aliveai] loaded " .. aliveai.loaded_objects .. ": " .. name)
end
minetest.register_craftitem("aliveai:npcspawner", {
description=minetest.colorize("black", "Random npc spawner") .. minetest.get_background_escape_sequence("pink"),
--description = "Random npc spawner",
inventory_image ="aliveai_rnd.png",
on_place = function(itemstack, user, pointed_thing)
if pointed_thing.type=="node" then
local pos=aliveai.roundpos(pointed_thing.above)
local list={}
local ii=1
for i, v in pairs(aliveai.registered_bots) do
if v.type=="npc" then
list[ii]={y=v.spawn_y,name=v.bot}
ii=ii+1
end
end
local bot=list[aliveai.random(1,ii)]
if not bot then return end
pos.y=pos.y+bot.y
minetest.add_entity(pos, aliveai.registered_bots[bot.name].bot):set_yaw(math.random(0,6.28))
itemstack:take_item()
end
return itemstack
end,
})
minetest.register_craftitem("aliveai:teampawner", {
--description = "Random npc team " .. aliveai.default_team .." spawner",
description=minetest.colorize("white", "Random team " .. aliveai.default_team .." spawner") .. minetest.get_background_escape_sequence("purple"),
inventory_image ="aliveai_rnd.png",
on_place = function(itemstack, user, pointed_thing)
if pointed_thing.type=="node" then
local pos=aliveai.roundpos(pointed_thing.above)
local list={}
local ii=1
for i, v in pairs(aliveai.registered_bots) do
if v.type=="npc" and v.team==aliveai.default_team then
list[ii]={y=v.spawn_y,name=v.bot}
ii=ii+1
end
end
local bot=list[aliveai.random(1,ii)]
if not bot then return end
pos.y=pos.y+bot.y
minetest.add_entity(pos, aliveai.registered_bots[bot.name].bot):set_yaw(math.random(0,6.28))
itemstack:take_item()
end
return itemstack
end,
})
minetest.register_craftitem("aliveai:ai_fake_item_spawner", {
description = "Fake spawner (bugfix item)",
inventory_image = "aliveai_rnd.png",
groups={not_in_creative_inventory=1},
on_place = function(itemstack, user, pointed_thing)
return itemstack
end
})
aliveai.convertdata=function(str,spl)
if type(str)=="string" then
local s1=str.split(str,"?")
local r={}
for _, s in ipairs(s1) do
local s2=s.split(s,"=")
if s2[2]~=nil and string.find(s2[2],"*")~=nil then -- tables
local d1=s2[2].split(s2[2],"*")
local inner={}
for nn, ss in pairs(d1) do
local d2=ss.split(ss," ")
if d2 and d2[1] then
if tonumber(d2[1])~=nil then d2[1]=tonumber(d2[1]) end -- table name n to n
if tonumber(d2[2])~=nil then
inner[d2[1]]=tonumber(d2[2])
else
inner[d2[1]]=d2[2]
end
end
end
r[s2[1]]=inner
elseif s2[2]~=nil and string.find(s2[2],",")~=nil then -- pos
r[s2[1]]=aliveai.strpos(s2[2],true)
else -- else
local tnr=tonumber(s2[2])
if tnr~=nil then s2[2]=tnr end
r[s2[1]]=s2[2]
end
end
return r
elseif type(str)=="table" then
print("WARNING: the function convertdata will be replaced, use minetest.deserialize and minetest.serialize instead")
if 1 then return "" end
local r=""
for n, s in pairs(str) do
r=r .. n .."="
if s and type(s)=="table" and s.x and s.y and s.z then -- pos
r=r .. aliveai.strpos(s,false)
elseif s and type(s)=="table" then -- table
for n1, s1 in pairs(s) do
if n1==nil or type(n1)=="table" or type(s1)=="table" then
n1=""
s1=""
print("Converting variable failure: tables in tables not allowed")
end
r=r .. n1 .. " " .. s1 .."*"
end
else -- else
r=r .. s
end
r=r .. "?"
end
return r
end
return ""
end