mod-slimes/api.lua

289 lines
8.8 KiB
Lua

slimes = {}
function slimes:register_slime (name, def)
local defbox = def.size/2
minetest.register_entity(name,{
initial_properties = {
name = name,
hp_max = def.max_hp,
visual_size = {x = def.size, y = def.size, z = def.size},
visual = "cube",
textures = def.textures, -- top, bottom, front, back, left, right
collisionbox = {-defbox, -defbox, -defbox, defbox, defbox, defbox},
physical = true,
},
alpha = 160,
timer = 6,
timer2 = 1,
timer3 = 0, --regularly check if slime touches ground and possibly set x/z velocity/acceleration to 0
yaw = 0,
direction = {},
status = 2, --1 = jump, 2 = rotate
found_target = false,
-- ON ACTIVATE --
on_activate = function(self)
self.object:setacceleration({x = 0, y = -def.gravity, z = 0})
end,
-- ON PUNCH --
on_punch = function(self)
local pos = self.object:getpos()
minetest.sound_play(def.sounds.damage.file, {pos = pos,gain = (def.sounds.damage.gain or 0.25)})
effect(pos, 20*math.random(), def.blood)
check_for_slime_death (self,def)
end,
-- ON STEP --
on_step = function(self, dtime)
self.timer2 = self.timer2 + dtime
local pos = self.object:getpos()
if self.status == 2 and (self.timer2 >= 0.5) then
self.timer2 = 1.2
self.status = 1
-- FIXME
if slime_lonely(pos) and not minetest.env:find_node_near(pos, 24, def.spawn) then
self.object:remove()
end
-- FIXME improve IA
local objs = minetest.env:get_objects_inside_radius(pos, 24)
local ppos = {}
self.found_target = false
self.yaw = math.random() * 360
for i, obj in ipairs(objs) do
if obj:is_player() and damage_enabled and not def.passive then self.found_target = obj break end
if self.found_target == false
and obj:get_luaentity()
and (obj:get_luaentity().name == "slimes:" .. def.class .. "biga"
or obj:get_luaentity().name == "slimes:" .. def.class .. "medium") then
self.found_target = obj
end
end
if self.found_target ~= false then
local target = self.found_target:getpos()
ppos = {x = target.x - pos.x, z = target.z - pos.z}
if ppos.x ~= 0 and ppos.z ~= 0 then --found itself as an object
self.yaw = math.abs(math.atan(ppos.x/ppos.z) - math.pi / 2)
if ppos.z < 0 then self.yaw = self.yaw + math.pi end
--self.found_target = true
end
end
self.object:setyaw(self.yaw)
self.object:set_properties({automatic_rotate = 0})
self.direction = {x = math.cos(self.yaw)*2, y = 6, z = math.sin(self.yaw)*2}
minetest.sound_play(def.sounds.jump.file, {pos = pos,gain = (def.sounds.jump.gain or 0.25)})
self.object:set_properties({visual_size = {x = def.size, y = def.size - (def.size/8), z = def.size}})
end
self.timer = self.timer + dtime
self.timer3 = self.timer3 + dtime
if self.timer2 > 1.3 and self.object:getvelocity().y == 0 then
self.object:setvelocity(self.direction)
self.object:setacceleration({x = self.direction.x/5, y = -def.gravity, z = self.direction.z/5})
self.timer2 = 0
self.object:set_properties({visual_size = {x = def.size, y = def.size + (def.size/8), z = def.size}})
end
if (self.timer >= 6
or (self.timer >= 1
and self.found_target ~= false))
and self.object:getvelocity().y == 0 then
self.timer = 0
self.timer2 = 0
self.status = 2
if self.found_target == false then self.object:set_properties({automatic_rotate = math.pi * 8}) end
minetest.sound_play(def.sounds.land.file, {pos = pos,gain = (def.sounds.land.gain or 0.25)})
local n = minetest.get_node(pos)
if def.footprint
and minetest.get_item_group(n.name, "water") == 0
then minetest.set_node(pos, {name=def.footprint}) end
effect(pos, 20*math.random(), def.blood)
self.object:set_properties({visual_size = {x = def.size, y = def.size - (def.size/8), z = def.size}})
if damage_enabled then
local tod = minetest.get_timeofday()
--FIXME water and lava damage detection is not working like it should.
-- sunlight damage
if def.light_damage and def.light_damage ~= 0
and minetest.get_item_group(n.name, "water") == 0 -- no sun damage in water
and pos.y > 0
and (minetest.get_node_light(pos) or 0) > 10 -- direct sunlight (was 4)
and tod > 0.2 and tod < 0.8 then
self.object:set_hp(self.object:get_hp()-def.light_damage)
effect(pos, 20*math.random(), "tnt_smoke.png")
minetest.chat_send_all("me derrito ".. (minetest.get_node_light(pos) or 0))
end
-- water damage
if def.water_damage and def.water_damage ~= 0
and minetest.get_item_group(n.name, "water") ~= 0 then
self.object:set_hp(self.object:get_hp()-def.water_damage)
effect(pos, 20*math.random(), "bubble.png")
end
-- lava damage
if def.lava_damage and def.lava_damage ~= 0
and minetest.get_item_group(n.name, "lava") ~= 0 then
self.object:set_hp(self.object:get_hp()-def.lava_damage)
effect(pos, 20*math.random(), "fire_basic_flame.png")
end
-- fall damage
if self.fall_damage == 1 and self.object:getvelocity().y == 0 then
local d = self.old_y - self.object:getpos().y
self.old_y = self.object:getpos().y
if d > 5 then
self.object:set_hp(self.object:get_hp() - math.floor(d - 5))
end
end
check_for_slime_death (self,def)
local objs = minetest.env:get_objects_inside_radius(pos, def.size*1.75)
for i, obj in ipairs(objs) do
if obj:is_player() and not def.passive then
obj:punch(self.object, 1.0, {full_punch_interval=1.0,damage_groups = {fleshy=def.damage}})
minetest.sound_play(def.sounds.attack.file, {pos = pos,gain = (def.sounds.attack.gain or 0.25)})
end
end
end
end
if self.timer3 > 0.07 then
local vel = self.object:getvelocity()
if vel.y == 0 and (vel.x ~= 0 or vel.z ~= 0) then
self.object:setvelocity({x = 0, y = 0, z = 0})
self.object:setacceleration({x = 0, y = -def.gravity, z = 0})
self.object:set_properties({visual_size = {x = def.size, y = def.size, z = def.size}})
end
self.timer3 = 0
end
end,
})
end
-- check if slime is alone
function slime_lonely (pos)
local objs = minetest.env:get_objects_inside_radius(pos, 32)
for i, obj in pairs(objs) do
if obj:is_player() then return false end
end
return true
end
-- check for death
function check_for_slime_death(self,def)
if self.object:get_hp() > 0 then return end
local pos = self.object:getpos()
pos.y = pos.y + 0.5
if (def.sounds.death.file ~= nil ) then minetest.sound_play(def.sounds.death.file, {pos = pos,gain = (def.sounds.death.gain or 0.25)}) end
self.object:remove()
local chance = def.drops.chance
if math.random(1, def.drops.chance+1) == 1 or def.drops.chance == 0 then
local min = def.drops.min
local max = def.drops.max
local num = math.floor(math.random(min, max+1))
if def.drops.type == "item" then
for i=1,num do minetest.env:add_item(pos, def.drop) end
end
if def.drops.type == "entity" then
for i=1,num do minetest.env:add_entity({x=pos.x, y=pos.y + (def.size*math.random()), z=pos.z + (def.size*math.random())}, def.drops.name) end
end
end
end
-- particle effects
function effect(pos, amount, texture)
minetest.add_particlespawner({
amount = amount,
time = 0.25,
minpos = pos,
maxpos = pos,
minvel = {x=-0, y=-2, z=-0},
maxvel = {x=2, y=2, z=2},
minacc = {x=-4, y=-4, z=-4},
maxacc = {x=4, y=4, z=4},
minexptime = 0.1,
maxexptime = 1,
minsize = 0.5,
maxsize = 1,
texture = texture,
})
end
-- spawn slimes
slimes.spawn = {}
function slimes:register_spawn(name, nodes, neighbors, max_light, min_light, chance, active_object_count, max_height)
slimes.spawn[name] = true
minetest.register_abm({
nodenames = nodes,
neighbors = neighbors,
interval = 30,
chance = chance,
action = function(pos, node, _, active_object_count_wider)
-- do not spawn if too many active in area
if active_object_count_wider > active_object_count
or not pos then
return
end
-- mobs cannot spawn inside protected areas
if minetest.is_protected(pos, "") then
return
end
-- spawn above node
pos.y = pos.y + 1
-- check if light and height levels are ok to spawn
local light = minetest.get_node_light(pos)
if not light or light > max_light or light < min_light
or pos.y > max_height then
return
end
-- are we spawning inside a solid node?
local nod = minetest.get_node_or_nil(pos)
if not nod or not minetest.registered_nodes[nod.name]
or minetest.registered_nodes[nod.name].walkable == true then
return
end
pos.y = pos.y + 1
nod = minetest.get_node_or_nil(pos)
if not nod or not minetest.registered_nodes[nod.name]
or minetest.registered_nodes[nod.name].walkable == true then
return
end
-- spawn mob half block higher
pos.y = pos.y - 0.5
minetest.add_entity(pos, name)
end
})
end