initial commit
parent
87ad5d3e55
commit
35a60019a1
|
@ -1,2 +1,7 @@
|
||||||
# minetest_giants
|
# minetest_giants
|
||||||
A giant-themed mobs mod based on behavior trees
|
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