initial commit
parent
87ad5d3e55
commit
35a60019a1
|
@ -1,2 +1,7 @@
|
|||
# minetest_giants
|
||||
A giant-themed mobs mod based on behavior trees
|
||||
|
||||
|
||||
Based on Mobs (Redo) by PilzAdam, KrupnovPavel, Zeg9 and TenPlus1
|
||||
https://forum.minetest.net/viewtopic.php?f=9&t=9917
|
||||
|
||||
|
|
|
@ -0,0 +1,372 @@
|
|||
|
||||
|
||||
bt = {
|
||||
reset={},
|
||||
tick={},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function tprint (tbl, indent)
|
||||
local formatting = ""
|
||||
if not indent then indent = 0 end
|
||||
if tbl == nil then
|
||||
print(formatting .. "nil")
|
||||
return
|
||||
end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ": "
|
||||
if type(v) == "table" then
|
||||
print(formatting)
|
||||
tprint(v, indent+1)
|
||||
elseif type(v) == 'boolean' then
|
||||
print(formatting .. tostring(v))
|
||||
elseif type(v) == 'function' then
|
||||
print(formatting .. "[function]")
|
||||
elseif type(v) == 'userdata' then
|
||||
print(formatting .. "[userdata]")
|
||||
else
|
||||
print(formatting .. v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function mkSelector(name, list)
|
||||
|
||||
return {
|
||||
name=name,
|
||||
kind="selector",
|
||||
current_kid=-1,
|
||||
kids=list,
|
||||
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function mkSequence(name, list)
|
||||
|
||||
return {
|
||||
name=name,
|
||||
kind="sequence",
|
||||
current_kid=-1,
|
||||
kids=list,
|
||||
|
||||
}
|
||||
|
||||
|
||||
end
|
||||
|
||||
function mkRepeat(name, what)
|
||||
|
||||
return {
|
||||
name=name,
|
||||
kind="repeat",
|
||||
kids = what,
|
||||
|
||||
}
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
bt.reset.selector = function(node, data)
|
||||
node.current_kid = -1
|
||||
end
|
||||
bt.reset.sequence = function(node, data)
|
||||
node.current_kid = -1
|
||||
end
|
||||
|
||||
bt.reset["repeat"] = function(node, data)
|
||||
|
||||
end
|
||||
|
||||
bt.tick["repeat"] = function(node, data)
|
||||
--tprint(node)
|
||||
local ret = bt.tick[node.kids[1].kind](node.kids[1], data)
|
||||
if ret ~= "running" then
|
||||
print("repeat resetting")
|
||||
|
||||
bt.reset[node.kids[1].kind](node.kids[1], data)
|
||||
end
|
||||
|
||||
print("repeat ending\n&&&&&")
|
||||
|
||||
return "success"
|
||||
end
|
||||
|
||||
|
||||
-- nodes never call :reset() on themselves
|
||||
|
||||
|
||||
bt.tick.selector = function(node, data)
|
||||
|
||||
local ret
|
||||
|
||||
if node.current_kid == -1 then
|
||||
node.current_kid = 1
|
||||
ret = "failed" -- trick reset into being run
|
||||
end
|
||||
|
||||
while node.current_kid <= table.getn(node.kids) do
|
||||
|
||||
local cn = node.kids[node.current_kid]
|
||||
|
||||
-- reset fresh nodes
|
||||
if ret == "failed" then
|
||||
bt.reset[cn.kind](cn, data)
|
||||
end
|
||||
|
||||
-- tick the current node
|
||||
ret = bt.tick[cn.kind](cn, data)
|
||||
print(" selector got status ["..ret.."] from kid "..node.current_kid)
|
||||
if ret == "running" or ret == "success" then
|
||||
return ret
|
||||
end
|
||||
|
||||
node.current_kid = node.current_kid + 1
|
||||
end
|
||||
|
||||
|
||||
return "failed"
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
bt.tick.sequence = function(node, data)
|
||||
|
||||
local ret
|
||||
|
||||
if node.current_kid == -1 then
|
||||
node.current_kid = 1
|
||||
ret = "failed" -- trick reset into being run
|
||||
end
|
||||
|
||||
while node.current_kid <= table.getn(node.kids) do
|
||||
|
||||
local cn = node.kids[node.current_kid]
|
||||
|
||||
-- reset fresh nodes
|
||||
if ret == "failed" then
|
||||
bt.reset[cn.kind](cn, data)
|
||||
end
|
||||
|
||||
-- tick the current node
|
||||
ret = bt.tick[cn.kind](cn, data)
|
||||
print(" selector got status ["..ret.."] from kid "..node.current_kid)
|
||||
if ret == "running" or ret == "failed" then
|
||||
return ret
|
||||
end
|
||||
|
||||
node.current_kid = node.current_kid + 1
|
||||
end
|
||||
|
||||
return "success"
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- distance on x-z plane
|
||||
function distance(a,b)
|
||||
local x = a.x - b.x
|
||||
local z = a.z - b.z
|
||||
|
||||
return math.abs(math.sqrt(x*x + z*z))
|
||||
end
|
||||
|
||||
|
||||
bt.reset.find_node_near = function(node, data)
|
||||
|
||||
local targetpos = minetest.find_node_near(data.pos, node.dist, node.sel)
|
||||
data.targetPos = targetpos
|
||||
end
|
||||
|
||||
bt.tick.find_node_near = function(node, data)
|
||||
if data.targetPos == nil then
|
||||
print("could not find node near")
|
||||
return "failed"
|
||||
end
|
||||
|
||||
return "success"
|
||||
end
|
||||
|
||||
function mkFindNodeNear(sel, dist)
|
||||
|
||||
return {
|
||||
name="find node near",
|
||||
kind="find_node_near",
|
||||
dist = dist,
|
||||
sel = sel,
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
bt.reset.approach = function(node, data)
|
||||
|
||||
if data.targetPos ~= nil then
|
||||
print("Approaching target ("..data.targetPos.x..","..data.targetPos.y..","..data.targetPos.z..")")
|
||||
data.mob.destination = data.targetPos
|
||||
else
|
||||
print("Approach: targetPos is nil")
|
||||
end
|
||||
end
|
||||
|
||||
bt.tick.approach = function(node, data)
|
||||
if data.targetPos == nil then
|
||||
return "failed"
|
||||
end
|
||||
|
||||
local d = distance(data.pos, data.targetPos)
|
||||
|
||||
print("dist: "..d)
|
||||
|
||||
if d <= node.dist then
|
||||
print("arrived at target")
|
||||
return "success"
|
||||
end
|
||||
|
||||
return "running"
|
||||
end
|
||||
|
||||
|
||||
function mkApproach(dist)
|
||||
|
||||
return {
|
||||
name="go to",
|
||||
kind="approach",
|
||||
dist=dist,
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
function mkTryApproach(dist)
|
||||
|
||||
return {
|
||||
name="try to go to",
|
||||
kind="try_approach",
|
||||
dist=dist,
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
bt.reset.try_approach = function(node, data)
|
||||
|
||||
node.last_d = nil
|
||||
|
||||
if data.targetPos ~= nil then
|
||||
print("Approaching target ("..data.targetPos.x..","..data.targetPos.y..","..data.targetPos.z..")")
|
||||
data.mob.destination = data.targetPos
|
||||
else
|
||||
print("Approach: targetPos is nil")
|
||||
end
|
||||
end
|
||||
|
||||
bt.tick.try_approach = function(node, data)
|
||||
if data.targetPos == nil then
|
||||
return "failed"
|
||||
end
|
||||
|
||||
local d = distance(data.pos, data.targetPos)
|
||||
|
||||
if d <= node.dist then
|
||||
print("arrived at target")
|
||||
node.last_d = nil
|
||||
return "success"
|
||||
end
|
||||
|
||||
|
||||
if node.last_d == nil then
|
||||
node.last_d = d
|
||||
else
|
||||
local dd = math.abs(node.last_d - d)
|
||||
print("dist: ".. dd)
|
||||
if dd < .02 then
|
||||
-- we're stuck
|
||||
node.last_d = nil
|
||||
return "failed"
|
||||
end
|
||||
end
|
||||
|
||||
print("dist: ".. math.abs(node.last_d - d))
|
||||
|
||||
|
||||
|
||||
return "running"
|
||||
end
|
||||
|
||||
|
||||
bt.reset.destroy = function(node, data)
|
||||
|
||||
end
|
||||
|
||||
bt.tick.destroy = function(node, data)
|
||||
print("Destroying target")
|
||||
if data.targetPos == nil then
|
||||
return "failed"
|
||||
end
|
||||
|
||||
minetest.set_node(data.targetPos, {name="air"})
|
||||
|
||||
return "success"
|
||||
end
|
||||
|
||||
|
||||
function mkDestroy()
|
||||
|
||||
return {
|
||||
name="destroy",
|
||||
kind="destroy",
|
||||
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bt.reset.bash_walls = function(node, data)
|
||||
|
||||
end
|
||||
|
||||
bt.tick.bash_walls = function(node, data)
|
||||
|
||||
local pos = minetest.find_node_near(data.pos, 2, {"default:wood"})
|
||||
if pos == nil then
|
||||
return "failed"
|
||||
end
|
||||
|
||||
minetest.set_node(pos, {name="air"})
|
||||
|
||||
return "success"
|
||||
end
|
||||
|
||||
|
||||
function mkBashWalls()
|
||||
|
||||
return {
|
||||
name="destroy",
|
||||
kind="bash_walls",
|
||||
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
|
||||
|
||||
|
||||
mobs:register_simple_mob("giants:giant", {
|
||||
type = "monster",
|
||||
passive = false,
|
||||
attack_type = "dogfight",
|
||||
reach = 2,
|
||||
damage = 1,
|
||||
hp_min = 4,
|
||||
hp_max = 20,
|
||||
armor = 100,
|
||||
collisionbox = {-0.35,-1.0,-0.35, 0.35,0.8,0.35},
|
||||
visual = "mesh",
|
||||
mesh = "character.b3d",
|
||||
drawtype = "front",
|
||||
textures = {
|
||||
{"mobs_npc.png"},
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1.5,
|
||||
run_velocity = 4,
|
||||
view_range = 15,
|
||||
jump = true,
|
||||
floats = 0,
|
||||
drops = {
|
||||
{name = "default:iron_lump",
|
||||
chance = 1, min = 3, max = 5},
|
||||
},
|
||||
water_damage = 0,
|
||||
lava_damage = 4,
|
||||
light_damage = 0,
|
||||
fear_height = 3,
|
||||
animation = {
|
||||
speed_normal = 30,
|
||||
speed_run = 30,
|
||||
stand_start = 0,
|
||||
stand_end = 79,
|
||||
walk_start = 168,
|
||||
walk_end = 187,
|
||||
run_start = 168,
|
||||
run_end = 187,
|
||||
punch_start = 200,
|
||||
punch_end = 219,
|
||||
},
|
||||
|
||||
pre_activate = function(self, s,d)
|
||||
self.bt = mkRepeat("root", {
|
||||
mkSequence("snuff torches", {
|
||||
mkFindNodeNear({"default:torch"}, 20),
|
||||
mkSelector("seek", {
|
||||
mkTryApproach(.8),
|
||||
mkBashWalls(),
|
||||
mkTryApproach(.8),
|
||||
}),
|
||||
mkDestroy(),
|
||||
|
||||
})
|
||||
})
|
||||
end
|
||||
})
|
||||
|
||||
mobs:register_spawn("giants:giant", {"default:desert_sand"}, 20, 0, 7000, 2, 31000)
|
||||
|
||||
mobs:register_egg("giants:giant", "Giant", "default_desert_sand.png", 1)
|
|
@ -0,0 +1,20 @@
|
|||
local path = minetest.get_modpath("giants")
|
||||
|
||||
-- Mob Api
|
||||
|
||||
dofile(path.."/api.lua")
|
||||
dofile(path.."/behavior.lua")
|
||||
dofile(path.."/simple_api.lua")
|
||||
|
||||
|
||||
|
||||
dofile(path.."/giant.lua")
|
||||
|
||||
|
||||
-- Mob Items
|
||||
--dofile(path.."/crafts.lua")
|
||||
|
||||
-- Spawner
|
||||
--dofile(path.."/spawner.lua")
|
||||
|
||||
print ("[MOD] Giants loaded")
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Krupnov Pavel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
Binary file not shown.
|
@ -0,0 +1,521 @@
|
|||
-- Mobs Api (16th January 2016)
|
||||
|
||||
function mob_goTo(self, pos)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- register mob function
|
||||
function mobs:register_simple_mob(name, def)
|
||||
|
||||
local btdata = {}
|
||||
|
||||
minetest.register_entity(name, {
|
||||
|
||||
stepheight = def.stepheight or 0.6,
|
||||
name = name,
|
||||
type = def.type,
|
||||
attack_type = def.attack_type,
|
||||
fly = def.fly,
|
||||
fly_in = def.fly_in or "air",
|
||||
owner = def.owner or "",
|
||||
order = def.order or "",
|
||||
on_die = def.on_die,
|
||||
do_custom = def.do_custom,
|
||||
jump_height = def.jump_height or 2,
|
||||
jump_chance = def.jump_chance or 0,
|
||||
drawtype = def.drawtype, -- DEPRECATED, use rotate instead
|
||||
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
|
||||
lifetimer = def.lifetimer or 180, -- 3 minutes
|
||||
hp_min = def.hp_min or 5,
|
||||
hp_max = def.hp_max or 10,
|
||||
physical = true,
|
||||
collisionbox = def.collisionbox,
|
||||
visual = def.visual,
|
||||
visual_size = def.visual_size or {x = 1, y = 1},
|
||||
mesh = def.mesh,
|
||||
makes_footstep_sound = def.makes_footstep_sound or false,
|
||||
view_range = def.view_range or 5,
|
||||
walk_velocity = def.walk_velocity or 1,
|
||||
run_velocity = def.run_velocity or 2,
|
||||
damage = def.damage or 0,
|
||||
light_damage = def.light_damage or 0,
|
||||
water_damage = def.water_damage or 0,
|
||||
lava_damage = def.lava_damage or 0,
|
||||
fall_damage = def.fall_damage or 1,
|
||||
fall_speed = def.fall_speed or -10, -- must be lower than -2 (default: -10)
|
||||
drops = def.drops or {},
|
||||
armor = def.armor,
|
||||
on_rightclick = def.on_rightclick,
|
||||
arrow = def.arrow,
|
||||
shoot_interval = def.shoot_interval,
|
||||
sounds = def.sounds or {},
|
||||
animation = def.animation,
|
||||
follow = def.follow,
|
||||
jump = def.jump or true,
|
||||
walk_chance = def.walk_chance or 50,
|
||||
attacks_monsters = def.attacks_monsters or false,
|
||||
group_attack = def.group_attack or false,
|
||||
--fov = def.fov or 120,
|
||||
passive = def.passive or false,
|
||||
recovery_time = def.recovery_time or 0.5,
|
||||
knock_back = def.knock_back or 3,
|
||||
blood_amount = def.blood_amount or 5,
|
||||
blood_texture = def.blood_texture or "mobs_blood.png",
|
||||
shoot_offset = def.shoot_offset or 0,
|
||||
floats = def.floats or 1, -- floats in water by default
|
||||
replace_rate = def.replace_rate,
|
||||
replace_what = def.replace_what,
|
||||
replace_with = def.replace_with,
|
||||
replace_offset = def.replace_offset or 0,
|
||||
timer = 0,
|
||||
env_damage_timer = 0, -- only used when state = "attack"
|
||||
tamed = false,
|
||||
pause_timer = 0,
|
||||
horny = false,
|
||||
hornytimer = 0,
|
||||
child = false,
|
||||
gotten = false,
|
||||
health = 0,
|
||||
reach = def.reach or 3,
|
||||
htimer = 0,
|
||||
child_texture = def.child_texture,
|
||||
docile_by_day = def.docile_by_day or false,
|
||||
time_of_day = 0.5,
|
||||
fear_height = def.fear_height or 0,
|
||||
runaway = def.runaway,
|
||||
runaway_timer = 0,
|
||||
destination = nil,
|
||||
|
||||
bt_timer = 0,
|
||||
|
||||
goTo = mob_goTo,
|
||||
|
||||
bt = nil,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
|
||||
local pos = self.object:getpos()
|
||||
local yaw = self.object:getyaw() or 0
|
||||
|
||||
btdata.pos = pos
|
||||
btdata.yaw = yaw
|
||||
btdata.mob = self
|
||||
|
||||
self.bt_timer = self.bt_timer + dtime
|
||||
--print("bt_timer "..self.bt_timer)
|
||||
|
||||
if self.bt_timer > 1 then
|
||||
|
||||
print("<<< start >>>")
|
||||
bt.tick[self.bt.kind](self.bt, btdata)
|
||||
print("<<< end >>>")
|
||||
|
||||
self.bt_timer = 0
|
||||
end
|
||||
|
||||
btdata.lastpos = pos
|
||||
|
||||
|
||||
if not self.fly then
|
||||
|
||||
-- floating in water (or falling)
|
||||
local v = self.object:getvelocity()
|
||||
|
||||
-- going up then apply gravity
|
||||
if v.y > 0.1 then
|
||||
|
||||
self.object:setacceleration({
|
||||
x = 0,
|
||||
y = self.fall_speed,
|
||||
z = 0
|
||||
})
|
||||
end
|
||||
|
||||
-- in water then float up
|
||||
if minetest.registered_nodes[node_ok(pos).name].groups.water then
|
||||
|
||||
if self.floats == 1 then
|
||||
|
||||
self.object:setacceleration({
|
||||
x = 0,
|
||||
y = -self.fall_speed / (math.max(1, v.y) ^ 2),
|
||||
z = 0
|
||||
})
|
||||
end
|
||||
else
|
||||
-- fall downwards
|
||||
self.object:setacceleration({
|
||||
x = 0,
|
||||
y = self.fall_speed,
|
||||
z = 0
|
||||
})
|
||||
|
||||
-- fall damage
|
||||
if self.fall_damage == 1
|
||||
and self.object:getvelocity().y == 0 then
|
||||
|
||||
local d = self.old_y - self.object:getpos().y
|
||||
|
||||
if d > 5 then
|
||||
|
||||
self.object:set_hp(self.object:get_hp() - math.floor(d - 5))
|
||||
|
||||
effect(pos, 5, "tnt_smoke.png")
|
||||
|
||||
if check_for_death(self) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self.old_y = self.object:getpos().y
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- -- knockback timer
|
||||
-- if self.pause_timer > 0 then
|
||||
--
|
||||
-- self.pause_timer = self.pause_timer - dtime
|
||||
--
|
||||
-- if self.pause_timer < 1 then
|
||||
-- self.pause_timer = 0
|
||||
-- end
|
||||
--
|
||||
-- return
|
||||
-- end
|
||||
|
||||
-- attack timer
|
||||
-- self.timer = self.timer + dtime
|
||||
|
||||
-- if self.state ~= "attack" then
|
||||
--
|
||||
-- if self.timer < 1 then
|
||||
-- return
|
||||
-- end
|
||||
--
|
||||
-- self.timer = 0
|
||||
-- end
|
||||
|
||||
-- never go over 100
|
||||
if self.timer > 100 then
|
||||
self.timer = 1
|
||||
end
|
||||
|
||||
-- node replace check (cow eats grass etc.)
|
||||
replace(self, pos)
|
||||
|
||||
-- mob plays random sound at times
|
||||
if self.sounds.random
|
||||
and math.random(1, 100) == 1 then
|
||||
|
||||
minetest.sound_play(self.sounds.random, {
|
||||
object = self.object,
|
||||
max_hear_distance = self.sounds.distance
|
||||
})
|
||||
end
|
||||
|
||||
-- environmental damage timer (every 1 second)
|
||||
self.env_damage_timer = self.env_damage_timer + dtime
|
||||
|
||||
if (self.state == "attack" and self.env_damage_timer > 1)
|
||||
or self.state ~= "attack" then
|
||||
|
||||
self.env_damage_timer = 0
|
||||
|
||||
do_env_damage(self)
|
||||
|
||||
-- custom function (defined in mob lua file)
|
||||
if self.do_custom then
|
||||
self.do_custom(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if self.destination ~= nil then
|
||||
|
||||
--print("destination ")
|
||||
|
||||
local dist = distance(pos, self.destination)
|
||||
-- print("walk dist ".. dist)
|
||||
local s = self.destination
|
||||
local vec = {
|
||||
x = pos.x - s.x,
|
||||
y = pos.y - s.y,
|
||||
z = pos.z - s.z
|
||||
}
|
||||
|
||||
-- tprint(vec)
|
||||
|
||||
if vec.x ~= 0
|
||||
or vec.z ~= 0 then
|
||||
|
||||
yaw = (math.atan(vec.z / vec.x) + math.pi / 2) - self.rotate
|
||||
|
||||
if s.x > pos.x then
|
||||
yaw = yaw + math.pi
|
||||
end
|
||||
|
||||
-- print("yaw " .. yaw)
|
||||
|
||||
self.object:setyaw(yaw)
|
||||
end
|
||||
|
||||
-- anyone but standing npc's can move along
|
||||
if dist > .1 then
|
||||
|
||||
if (self.jump
|
||||
and get_velocity(self) <= 0.5
|
||||
and self.object:getvelocity().y == 0)
|
||||
or (self.object:getvelocity().y == 0
|
||||
and self.jump_chance > 0) then
|
||||
do_jump(self)
|
||||
end
|
||||
|
||||
|
||||
|
||||
set_velocity(self, self.walk_velocity)
|
||||
set_animation(self, "walk")
|
||||
else
|
||||
-- we have arrived
|
||||
self.destination = nil
|
||||
|
||||
set_velocity(self, 0)
|
||||
set_animation(self, "stand")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
end,
|
||||
|
||||
on_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
||||
|
||||
-- weapon wear
|
||||
local weapon = hitter:get_wielded_item()
|
||||
local punch_interval = 1.4
|
||||
|
||||
if tool_capabilities then
|
||||
punch_interval = tool_capabilities.full_punch_interval or 1.4
|
||||
end
|
||||
|
||||
if weapon:get_definition()
|
||||
and weapon:get_definition().tool_capabilities then
|
||||
|
||||
weapon:add_wear(math.floor((punch_interval / 75) * 9000))
|
||||
hitter:set_wielded_item(weapon)
|
||||
end
|
||||
|
||||
-- weapon sounds
|
||||
if weapon:get_definition().sounds ~= nil then
|
||||
|
||||
local s = math.random(0, #weapon:get_definition().sounds)
|
||||
|
||||
minetest.sound_play(weapon:get_definition().sounds[s], {
|
||||
object = hitter,
|
||||
max_hear_distance = 8
|
||||
})
|
||||
else
|
||||
minetest.sound_play("default_punch", {
|
||||
object = hitter,
|
||||
max_hear_distance = 5
|
||||
})
|
||||
end
|
||||
|
||||
-- exit here if dead
|
||||
if check_for_death(self) then
|
||||
return
|
||||
end
|
||||
|
||||
-- blood_particles
|
||||
if self.blood_amount > 0
|
||||
and not disable_blood then
|
||||
|
||||
local pos = self.object:getpos()
|
||||
|
||||
pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) / 2
|
||||
|
||||
effect(pos, self.blood_amount, self.blood_texture)
|
||||
end
|
||||
|
||||
-- knock back effect
|
||||
if self.knock_back > 0 then
|
||||
|
||||
local v = self.object:getvelocity()
|
||||
local r = 1.4 - math.min(punch_interval, 1.4)
|
||||
local kb = r * 5
|
||||
|
||||
self.object:setvelocity({
|
||||
x = (dir.x or 0) * kb,
|
||||
y = 2,
|
||||
z = (dir.z or 0) * kb
|
||||
})
|
||||
|
||||
self.pause_timer = r
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
|
||||
|
||||
btdata.lastpos = self.object:getpos()
|
||||
|
||||
if type(def.pre_activate) == "function" then
|
||||
def.pre_activate(self, static_data, dtime_s)
|
||||
end
|
||||
|
||||
-- load entity variables
|
||||
if staticdata then
|
||||
|
||||
local tmp = minetest.deserialize(staticdata)
|
||||
|
||||
if tmp then
|
||||
|
||||
for _,stat in pairs(tmp) do
|
||||
self[_] = stat
|
||||
end
|
||||
end
|
||||
else
|
||||
self.object:remove()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- select random texture, set model and size
|
||||
if not self.base_texture then
|
||||
|
||||
self.base_texture = def.textures[math.random(1, #def.textures)]
|
||||
self.base_mesh = def.mesh
|
||||
self.base_size = self.visual_size
|
||||
self.base_colbox = self.collisionbox
|
||||
end
|
||||
|
||||
-- set texture, model and size
|
||||
local textures = self.base_texture
|
||||
local mesh = self.base_mesh
|
||||
local vis_size = self.base_size
|
||||
local colbox = self.base_colbox
|
||||
|
||||
-- specific texture if gotten
|
||||
if self.gotten == true
|
||||
and def.gotten_texture then
|
||||
textures = def.gotten_texture
|
||||
end
|
||||
|
||||
-- specific mesh if gotten
|
||||
if self.gotten == true
|
||||
and def.gotten_mesh then
|
||||
mesh = def.gotten_mesh
|
||||
end
|
||||
|
||||
-- set child objects to half size
|
||||
if self.child == true then
|
||||
|
||||
vis_size = {
|
||||
x = self.base_size.x / 2,
|
||||
y = self.base_size.y / 2
|
||||
}
|
||||
|
||||
if def.child_texture then
|
||||
textures = def.child_texture[1]
|
||||
end
|
||||
|
||||
colbox = {
|
||||
self.base_colbox[1] / 2,
|
||||
self.base_colbox[2] / 2,
|
||||
self.base_colbox[3] / 2,
|
||||
self.base_colbox[4] / 2,
|
||||
self.base_colbox[5] / 2,
|
||||
self.base_colbox[6] / 2
|
||||
}
|
||||
end
|
||||
|
||||
if self.health == 0 then
|
||||
self.health = math.random (self.hp_min, self.hp_max)
|
||||
end
|
||||
|
||||
self.object:set_hp(self.health)
|
||||
self.object:set_armor_groups({fleshy = self.armor})
|
||||
self.old_y = self.object:getpos().y
|
||||
self.object:setyaw(math.random(1, 360) / 180 * math.pi)
|
||||
self.sounds.distance = (self.sounds.distance or 10)
|
||||
self.textures = textures
|
||||
self.mesh = mesh
|
||||
self.collisionbox = colbox
|
||||
self.visual_size = vis_size
|
||||
|
||||
-- set anything changed above
|
||||
self.object:set_properties(self)
|
||||
update_tag(self)
|
||||
|
||||
if type(def.post_activate) == "function" then
|
||||
def.post_activate(self, static_data, dtime_s)
|
||||
end
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
|
||||
-- remove mob when out of range unless tamed
|
||||
if mobs.remove
|
||||
and self.remove_ok
|
||||
and not self.tamed then
|
||||
|
||||
--print ("REMOVED", self.remove_ok, self.name)
|
||||
|
||||
self.object:remove()
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
self.remove_ok = true
|
||||
self.attack = nil
|
||||
self.following = nil
|
||||
self.state = "stand"
|
||||
|
||||
-- used to rotate older mobs
|
||||
if self.drawtype
|
||||
and self.drawtype == "side" then
|
||||
self.rotate = math.rad(90)
|
||||
end
|
||||
|
||||
local tmp = {}
|
||||
|
||||
for _,stat in pairs(self) do
|
||||
|
||||
local t = type(stat)
|
||||
|
||||
if t ~= 'function'
|
||||
and t ~= 'nil'
|
||||
and t ~= 'userdata' then
|
||||
tmp[_] = self[_]
|
||||
end
|
||||
end
|
||||
|
||||
-- print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n')
|
||||
return minetest.serialize(tmp)
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
end -- END mobs:register_mob function
|
||||
|
||||
|
||||
|
||||
|
||||
-- set content id's
|
||||
local c_air = minetest.get_content_id("air")
|
||||
local c_ignore = minetest.get_content_id("ignore")
|
||||
local c_obsidian = minetest.get_content_id("default:obsidian")
|
||||
local c_brick = minetest.get_content_id("default:obsidianbrick")
|
||||
local c_chest = minetest.get_content_id("default:chest_locked")
|
||||
|
||||
|
Binary file not shown.
|
@ -0,0 +1,127 @@
|
|||
-- mob spawner
|
||||
|
||||
local spawner_default = "mobs:pumba 10 15 0"
|
||||
|
||||
minetest.register_node("mobs:spawner", {
|
||||
tiles = {"mob_spawner.png"},
|
||||
drawtype = "glasslike",
|
||||
paramtype = "light",
|
||||
walkable = true,
|
||||
description = "Mob Spawner",
|
||||
groups = {cracky = 1},
|
||||
|
||||
on_construct = function(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
||||
-- text entry formspec
|
||||
meta:set_string("formspec", "field[text;mob_name min_light max_light amount;${command}]")
|
||||
meta:set_string("infotext", "Spawner Not Active (enter settings)")
|
||||
meta:set_string("command", spawner_default)
|
||||
end,
|
||||
|
||||
on_right_click = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
if not fields.text or fields.text == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local comm = fields.text:split(" ")
|
||||
local name = sender:get_player_name()
|
||||
|
||||
if minetest.is_protected(pos, name) then
|
||||
minetest.record_protection_violation(pos, name)
|
||||
return
|
||||
end
|
||||
|
||||
local mob = comm[1]
|
||||
local mlig = tonumber(comm[2])
|
||||
local xlig = tonumber(comm[3])
|
||||
local num = tonumber(comm[4])
|
||||
|
||||
if mob and mob ~= ""
|
||||
and num and num >= 0 and num <= 10
|
||||
and mlig and mlig >= 0 and mlig <= 15
|
||||
and xlig and xlig >= 0 and xlig <= 15 then
|
||||
|
||||
meta:set_string("command", fields.text)
|
||||
meta:set_string("infotext", "Spawner Active (" .. mob .. ")")
|
||||
|
||||
else
|
||||
minetest.chat_send_player(name, "Mob Spawner settings failed!")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- spawner abm
|
||||
minetest.register_abm({
|
||||
nodenames = {"mobs:spawner"},
|
||||
interval = 10,
|
||||
chance = 4,
|
||||
catch_up = false,
|
||||
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
|
||||
-- check objects inside 9x9 area around spawner
|
||||
local objs = minetest.get_objects_inside_radius(pos, 9)
|
||||
|
||||
-- get meta and command
|
||||
local meta = minetest.get_meta(pos)
|
||||
local comm = meta:get_string("command"):split(" ")
|
||||
|
||||
-- get settings from command
|
||||
local mob = comm[1]
|
||||
local mlig = tonumber(comm[2])
|
||||
local xlig = tonumber(comm[3])
|
||||
local num = tonumber(comm[4])
|
||||
|
||||
-- if amount is 0 then do nothing
|
||||
if num == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local count = 0
|
||||
local ent = nil
|
||||
|
||||
-- count objects of same type in area
|
||||
for k, obj in pairs(objs) do
|
||||
|
||||
ent = obj:get_luaentity()
|
||||
|
||||
if ent and ent.name == mob then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- is there too many of same type?
|
||||
if count >= num then
|
||||
return
|
||||
end
|
||||
|
||||
-- find air blocks within 5 nodes of spawner
|
||||
local air = minetest.find_nodes_in_area(
|
||||
{x = pos.x - 5, y = pos.y, z = pos.z - 5},
|
||||
{x = pos.x + 5, y = pos.y, z = pos.z + 5},
|
||||
{"air"})
|
||||
|
||||
-- spawn in random air block
|
||||
if air and #air > 0 then
|
||||
|
||||
local pos2 = air[math.random(#air)]
|
||||
local lig = minetest.get_node_light(pos2)
|
||||
|
||||
pos2.y = pos2.y + 0.5
|
||||
|
||||
-- only if light levels are within range
|
||||
if lig and lig >= mlig and lig <= xlig then
|
||||
minetest.add_entity(pos2, mob)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
})
|
Binary file not shown.
After Width: | Height: | Size: 108 B |
Binary file not shown.
After Width: | Height: | Size: 267 B |
Binary file not shown.
After Width: | Height: | Size: 901 B |
Binary file not shown.
After Width: | Height: | Size: 1018 B |
Binary file not shown.
After Width: | Height: | Size: 684 B |
Loading…
Reference in New Issue