Code tweak and tidy, mob counter attempt

This commit is contained in:
tenplus1 2020-05-15 13:26:34 +01:00
parent b610a81a58
commit 53cc10a16f
1 changed files with 249 additions and 149 deletions

398
api.lua
View File

@ -6,15 +6,21 @@ local use_cmi = minetest.global_exists("cmi")
mobs = { mobs = {
mod = "redo", mod = "redo",
version = "20200513", version = "20200515",
intllib = S, intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {} invis = minetest.global_exists("invisibility") and invisibility or {}
} }
-- mob table and limit (not active)
local active_mobs = 0
local active_limit = 99
-- creative check -- creative check
local creative_cache = minetest.settings:get_bool("creative_mode") local creative_cache = minetest.settings:get_bool("creative_mode")
function mobs.is_creative(name) function mobs.is_creative(name)
return creative_cache or minetest.check_player_privs(name, {creative = true}) return creative_cache or minetest.check_player_privs(name,
{creative = true})
end end
@ -68,7 +74,7 @@ local aoc_range = tonumber(minetest.settings:get("active_block_range")) * 16
-- pathfinding settings -- pathfinding settings
local enable_pathfinding = true local enable_pathfinding = true
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching local stuck_timeout = 3 -- how long before stuck mod starts searching
local stuck_path_timeout = 10 -- how long will mob follow path before giving up local stuck_path_timeout = 10 -- how long will mob follow path before giving up
-- default nodes -- default nodes
@ -337,7 +343,8 @@ function mob_class:set_animation(anim, force)
self.object:set_animation({ self.object:set_animation({
x = self.animation[anim .. "_start"], x = self.animation[anim .. "_start"],
y = self.animation[anim .. "_end"]}, y = self.animation[anim .. "_end"]},
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15, self.animation[anim .. "_speed"] or
self.animation.speed_normal or 15,
0, self.animation[anim .. "_loop"] ~= false) 0, self.animation[anim .. "_loop"] ~= false)
end end
@ -468,18 +475,16 @@ local ray_line_of_sight = function(self, pos1, pos2)
local ray = minetest.raycast(pos1, pos2, true, false) local ray = minetest.raycast(pos1, pos2, true, false)
local thing = ray:next() local thing = ray:next()
while thing do while thing do -- thing.type, thing.ref
-- if thing.type == "object"
-- and thing.ref ~= self.object
-- and not thing.ref:is_player() then return false end
if thing.type == "node" then if thing.type == "node" then
local name = minetest.get_node(thing.under).name local name = minetest.get_node(thing.under).name
if minetest.registered_items[name] if minetest.registered_items[name]
and minetest.registered_items[name].walkable then return false end and minetest.registered_items[name].walkable then
return false
end
end end
thing = ray:next() thing = ray:next()
@ -521,8 +526,7 @@ function mob_class:attempt_flight_correction(override)
local flyable_nodes = minetest.find_nodes_in_area( local flyable_nodes = minetest.find_nodes_in_area(
{x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
{x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, searchnodes)
searchnodes)
if #flyable_nodes < 1 then if #flyable_nodes < 1 then
return false return false
@ -595,7 +599,7 @@ function mobs:yaw_to_pos(self, target, rot)
end end
-- if self.stay_near set then check periodically for nodes and turn to face/move -- if stay near set then check periodically for nodes and turn towards them
function mob_class:do_stay_near() function mob_class:do_stay_near()
if not self.stay_near then return false end if not self.stay_near then return false end
@ -632,8 +636,8 @@ end
-- custom particle effects -- custom particle effects
local effect = function( local effect = function(pos, amount, texture, min_size, max_size,
pos, amount, texture, min_size, max_size, radius, gravity, glow, fall) radius, gravity, glow, fall)
radius = radius or 2 radius = radius or 2
min_size = min_size or 0.5 min_size = min_size or 0.5
@ -695,7 +699,8 @@ function mob_class:item_drop()
local pos = self.object:get_pos() local pos = self.object:get_pos()
-- check for drops function -- check for drops function
self.drops = type(self.drops) == "function" and self.drops(pos) or self.drops self.drops = type(self.drops) == "function"
and self.drops(pos) or self.drops
-- check for nil or no drops -- check for nil or no drops
if not self.drops or #self.drops == 0 then if not self.drops or #self.drops == 0 then
@ -703,7 +708,8 @@ function mob_class:item_drop()
end end
-- was mob killed by player? -- was mob killed by player?
local death_by_player = self.cause_of_death and self.cause_of_death.puncher local death_by_player = self.cause_of_death
and self.cause_of_death.puncher
and self.cause_of_death.puncher:is_player() or nil and self.cause_of_death.puncher:is_player() or nil
local obj, item, num local obj, item, num
@ -752,6 +758,23 @@ function mob_class:item_drop()
end end
-- remove mob and descrease counter
local remove_mob = function(self, decrease)
self.object:remove()
if decrease then
-- active_mobs = active_mobs - 1
if active_mobs < 0 then
active_mobs = 0
end
end
--print("-- active mobs: " .. active_mobs .. " / " .. active_limit)
end
-- check if mob is dead or only hurt -- check if mob is dead or only hurt
function mob_class:check_for_death(cmi_cause) function mob_class:check_for_death(cmi_cause)
@ -812,7 +835,7 @@ function mob_class:check_for_death(cmi_cause)
cmi.notify_die(self.object, cmi_cause) cmi.notify_die(self.object, cmi_cause)
end end
self.object:remove() remove_mob(self, true)
return true return true
end end
@ -841,7 +864,8 @@ function mob_class:check_for_death(cmi_cause)
cmi.notify_die(self.object, cmi_cause) cmi.notify_die(self.object, cmi_cause)
end end
self.object:remove() remove_mob(self, true)
end, self) end, self)
else else
@ -849,7 +873,7 @@ function mob_class:check_for_death(cmi_cause)
cmi.notify_die(self.object, cmi_cause) cmi.notify_die(self.object, cmi_cause)
end end
self.object:remove() remove_mob(self, true)
end end
effect(pos, 20, "tnt_smoke.png") effect(pos, 20, "tnt_smoke.png")
@ -955,10 +979,10 @@ function mob_class:do_env_damage()
self.time_of_day = minetest.get_timeofday() self.time_of_day = minetest.get_timeofday()
-- remove mob if standing inside ignore node -- halt mob if standing inside ignore node
if self.standing_in == "ignore" then if self.standing_in == "ignore" then
self.object:remove() self.object:set_velocity(0)
return true return true
end end
@ -975,7 +999,9 @@ function mob_class:do_env_damage()
effect(pos, 5, "tnt_smoke.png") effect(pos, 5, "tnt_smoke.png")
if self:check_for_death({type = "light"}) then return true end if self:check_for_death({type = "light"}) then
return true
end
end end
end end
@ -993,7 +1019,9 @@ function mob_class:do_env_damage()
effect(pos, 5, "bubble.png", nil, nil, 1, nil) effect(pos, 5, "bubble.png", nil, nil, 1, nil)
if self:check_for_death({type = "environment", if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then return true end pos = pos, node = self.standing_in}) then
return true
end
end end
-- ignition source (fire or lava) -- ignition source (fire or lava)
@ -1006,7 +1034,9 @@ function mob_class:do_env_damage()
effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
if self:check_for_death({type = "environment", pos = pos, if self:check_for_death({type = "environment", pos = pos,
node = self.standing_in, hot = true}) then return true end node = self.standing_in, hot = true}) then
return true
end
end end
-- damage_per_second node check -- damage_per_second node check
@ -1017,7 +1047,9 @@ function mob_class:do_env_damage()
effect(pos, 5, "tnt_smoke.png") effect(pos, 5, "tnt_smoke.png")
if self:check_for_death({type = "environment", if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then return true end pos = pos, node = self.standing_in}) then
return true
end
end end
--- suffocation inside solid node --- suffocation inside solid node
@ -1038,7 +1070,9 @@ function mob_class:do_env_damage()
self.health = self.health - damage self.health = self.health - damage
if self:check_for_death({type = "suffocation", if self:check_for_death({type = "suffocation",
pos = pos, node = self.standing_in}) then return true end pos = pos, node = self.standing_in}) then
return true
end
end end
return self:check_for_death({type = "unknown"}) return self:check_for_death({type = "unknown"})
@ -1076,7 +1110,7 @@ function mob_class:do_jump()
local nod = node_ok(pos) local nod = node_ok(pos)
--print ("standing on:", nod.name, pos.y) --print("standing on:", nod.name, pos.y)
if minetest.registered_nodes[nod.name].walkable == false then if minetest.registered_nodes[nod.name].walkable == false then
return false return false
@ -1102,8 +1136,8 @@ function mob_class:do_jump()
local blocked = minetest.registered_nodes[nodt.name].walkable local blocked = minetest.registered_nodes[nodt.name].walkable
--print ("in front:", nod.name, pos.y + 0.5) --print("in front:", nod.name, pos.y + 0.5)
--print ("in front above:", nodt.name, pos.y + 1.5) --print("in front above:", nodt.name, pos.y + 1.5)
-- jump if standing on solid node (not snow) and not blocked above -- jump if standing on solid node (not snow) and not blocked above
if (self.walk_chance == 0 if (self.walk_chance == 0
@ -1326,6 +1360,15 @@ function mob_class:breed()
self.hornytimer = 41 self.hornytimer = 41
ent.hornytimer = 41 ent.hornytimer = 41
-- have we reached active mob limit
if active_mobs >= active_limit then
minetest.chat_send_player(self.owner,
S("Active Mob Limit Reached!")
.. " (" .. active_mobs
.. " / " .. active_limit .. ")")
return
end
-- spawn baby -- spawn baby
minetest.after(5, function(self, ent) minetest.after(5, function(self, ent)
@ -1348,6 +1391,8 @@ function mob_class:breed()
local ent2 = mob:get_luaentity() local ent2 = mob:get_luaentity()
local textures = self.base_texture local textures = self.base_texture
-- active_mobs = active_mobs + 1
-- using specific child texture (if found) -- using specific child texture (if found)
if self.child_texture then if self.child_texture then
textures = self.child_texture[1] textures = self.child_texture[1]
@ -1423,7 +1468,7 @@ function mob_class:replace(pos)
if #minetest.find_nodes_in_area(pos, pos, what) > 0 then if #minetest.find_nodes_in_area(pos, pos, what) > 0 then
-- print ("replace node = ".. minetest.get_node(pos).name, pos.y) -- print("replace node = ".. minetest.get_node(pos).name, pos.y)
if self.on_replace then if self.on_replace then
@ -1602,7 +1647,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
--[[ --[[
-- show path using particles -- show path using particles
if self.path.way and #self.path.way > 0 then if self.path.way and #self.path.way > 0 then
print ("-- path length:" .. tonumber(#self.path.way)) print("-- path length:" .. tonumber(#self.path.way))
for _,pos in pairs(self.path.way) do for _,pos in pairs(self.path.way) do
minetest.add_particle({ minetest.add_particle({
pos = pos, pos = pos,
@ -1774,7 +1819,7 @@ function mob_class:general_attack()
-- are we a player? -- are we a player?
if objs[n]:is_player() then if objs[n]:is_player() then
-- if player invisible or mob not setup to attack then remove from list -- if player invisible or mob cannot attack then remove from list
if self.attack_players == false if self.attack_players == false
or (self.owner and self.type ~= "monster") or (self.owner and self.type ~= "monster")
or mobs.invis[objs[n]:get_player_name()] or mobs.invis[objs[n]:get_player_name()]
@ -2264,14 +2309,14 @@ function mob_class:do_states(dtime)
self.blinktimer = 0 self.blinktimer = 0
self:mob_sound(self.sounds.fuse) self:mob_sound(self.sounds.fuse)
--print ("=== explosion timer started", self.explosion_timer) --print("=== explosion timer started", self.explosion_timer)
-- stop timer if out of reach or direct line of sight -- stop timer if out of reach or direct line of sight
elseif self.allow_fuse_reset elseif self.allow_fuse_reset
and self.v_start and self.v_start
and (dist > self.reach or not self:line_of_sight(s, p, 2)) then and (dist > self.reach or not self:line_of_sight(s, p, 2)) then
--print ("=== explosion timer stopped") --print("=== explosion timer stopped")
self.v_start = false self.v_start = false
self.timer = 0 self.timer = 0
@ -2311,7 +2356,7 @@ function mob_class:do_states(dtime)
self.blinkstatus = not self.blinkstatus self.blinkstatus = not self.blinkstatus
end end
--print ("=== explosion timer", self.timer) --print("=== explosion timer", self.timer)
if self.timer > self.explosion_timer then if self.timer > self.explosion_timer then
@ -2324,7 +2369,7 @@ function mob_class:do_states(dtime)
node_break_radius = 1 node_break_radius = 1
end end
self.object:remove() remove_mob(self, true)
if minetest.get_modpath("tnt") and tnt and tnt.boom if minetest.get_modpath("tnt") and tnt and tnt.boom
and not minetest.is_protected(pos, "") then and not minetest.is_protected(pos, "") then
@ -2343,7 +2388,9 @@ function mob_class:do_states(dtime)
}) })
entity_physics(pos, entity_damage_radius) entity_physics(pos, entity_damage_radius)
effect(pos, 32, "tnt_smoke.png", nil, nil, node_break_radius, 1, 0)
effect(pos, 32, "tnt_smoke.png", nil, nil,
node_break_radius, 1, 0)
end end
return true return true
@ -2643,7 +2690,7 @@ end
local tr = minetest.get_modpath("toolranks") local tr = minetest.get_modpath("toolranks")
-- deal damage and effects when mob punched -- deal damage and effects when mob punched
function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- mob health check -- mob health check
if self.health <= 0 then if self.health <= 0 then
@ -2658,14 +2705,19 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
-- error checking when mod profiling is enabled -- error checking when mod profiling is enabled
if not tool_capabilities then if not tool_capabilities then
minetest.log("warning", "[mobs] Mod profiling enabled, damage not enabled") minetest.log("warning",
"[mobs] Mod profiling enabled, damage not enabled")
return return
end end
-- is mob protected? -- is mob protected?
if self.protected and hitter:is_player() if self.protected and hitter:is_player()
and minetest.is_protected(self.object:get_pos(), hitter:get_player_name()) then and minetest.is_protected(self.object:get_pos(),
minetest.chat_send_player(hitter:get_player_name(), S("Mob has been protected!")) hitter:get_player_name()) then
minetest.chat_send_player(hitter:get_player_name(),
S("Mob has been protected!"))
return return
end end
@ -2710,20 +2762,20 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
damage = self.immune_to[n][2] or 0 damage = self.immune_to[n][2] or 0
break break
-- if "all" then no tool does damage unless it's specified in list -- if "all" then no tools deal damage unless it's specified in list
elseif self.immune_to[n][1] == "all" then elseif self.immune_to[n][1] == "all" then
damage = self.immune_to[n][2] or 0 damage = self.immune_to[n][2] or 0
end end
end end
--print("Mob Damage is", damage)
-- healing -- healing
if damage <= -1 then if damage <= -1 then
self.health = self.health - floor(damage) self.health = self.health - floor(damage)
return return true
end end
--print ("Mob Damage is", damage)
if use_cmi if use_cmi
and cmi.notify_punch( and cmi.notify_punch(
self.object, hitter, tflp, tool_capabilities, dir, damage) then self.object, hitter, tflp, tool_capabilities, dir, damage) then
@ -2781,7 +2833,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
local blood = self.blood_texture local blood = self.blood_texture
local amount = self.blood_amount local amount = self.blood_amount
pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) * .5 pos.y = pos.y + (-self.collisionbox[2]
+ self.collisionbox[5]) * .5
-- lots of damage = more blood :) -- lots of damage = more blood :)
if damage > 10 then if damage > 10 then
@ -2806,10 +2859,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
if self:check_for_death({type = "punch", if self:check_for_death({type = "punch",
puncher = hitter, hot = hot}) then puncher = hitter, hot = hot}) then
return return true
end end
--[[ add healthy afterglow when hit (can cause hit lag with larger textures) --[[ add healthy afterglow when hit (causes lag with large textures)
minetest.after(0.1, function() minetest.after(0.1, function()
if not self.object:get_luaentity() then return end if not self.object:get_luaentity() then return end
@ -2924,9 +2977,9 @@ function mob_class:mob_staticdata()
and not self.tamed and not self.tamed
and self.lifetimer < 20000 then and self.lifetimer < 20000 then
--print ("REMOVED " .. self.name) --print("REMOVED " .. self.name)
self.object:remove() remove_mob(self, true)
return minetest.serialize({remove_ok = true, static_save = true}) return minetest.serialize({remove_ok = true, static_save = true})
end end
@ -2937,8 +2990,7 @@ function mob_class:mob_staticdata()
self.state = "stand" self.state = "stand"
-- used to rotate older mobs -- used to rotate older mobs
if self.drawtype if self.drawtype and self.drawtype == "side" then
and self.drawtype == "side" then
self.rotate = math.rad(90) self.rotate = math.rad(90)
end end
@ -2971,14 +3023,21 @@ end
function mob_class:mob_activate(staticdata, def, dtime) function mob_class:mob_activate(staticdata, def, dtime)
-- remove monsters in peaceful mode -- remove monsters in peaceful mode
if self.type == "monster" if self.type == "monster" and peaceful_only then
and peaceful_only then
self.object:remove() remove_mob(self, true)
return return
end end
-- remove mob if not tamed and mob total reached
if active_mobs >= active_limit and not self.tamed then
remove_mob(self)
--print("-- mob limit reached, removing " .. self.name)
return
end
-- load entity variables -- load entity variables
local tmp = minetest.deserialize(staticdata) local tmp = minetest.deserialize(staticdata)
@ -2988,6 +3047,13 @@ function mob_class:mob_activate(staticdata, def, dtime)
end end
end end
-- add currently spawned mobs to total
-- this is buggy as it doubles count when mobs unloaded and reloaded
if self.standing_in then
-- active_mobs = active_mobs + 1
--print("-- active mobs: " .. active_mobs .. " / " .. active_limit)
end
-- force current model into mob -- force current model into mob
self.mesh = def.mesh self.mesh = def.mesh
self.base_mesh = def.mesh self.base_mesh = def.mesh
@ -3002,7 +3068,8 @@ function mob_class:mob_activate(staticdata, def, dtime)
def.textures = {def.textures} def.textures = {def.textures}
end end
self.base_texture = def.textures and def.textures[random(1, #def.textures)] self.base_texture = def.textures and
def.textures[random(1, #def.textures)]
self.base_mesh = def.mesh self.base_mesh = def.mesh
self.base_size = self.visual_size self.base_size = self.visual_size
self.base_colbox = self.collisionbox self.base_colbox = self.collisionbox
@ -3022,14 +3089,12 @@ function mob_class:mob_activate(staticdata, def, dtime)
local selbox = self.base_selbox local selbox = self.base_selbox
-- specific texture if gotten -- specific texture if gotten
if self.gotten == true if self.gotten == true and def.gotten_texture then
and def.gotten_texture then
textures = def.gotten_texture textures = def.gotten_texture
end end
-- specific mesh if gotten -- specific mesh if gotten
if self.gotten == true if self.gotten == true and def.gotten_mesh then
and def.gotten_mesh then
mesh = def.gotten_mesh mesh = def.gotten_mesh
end end
@ -3046,21 +3111,14 @@ function mob_class:mob_activate(staticdata, def, dtime)
end end
colbox = { colbox = {
self.base_colbox[1] * .5, self.base_colbox[1] * .5, self.base_colbox[2] * .5,
self.base_colbox[2] * .5, self.base_colbox[3] * .5, self.base_colbox[4] * .5,
self.base_colbox[3] * .5, self.base_colbox[5] * .5, self.base_colbox[6] * .5}
self.base_colbox[4] * .5,
self.base_colbox[5] * .5,
self.base_colbox[6] * .5
}
selbox = { selbox = {
self.base_selbox[1] * .5, self.base_selbox[1] * .5, self.base_selbox[2] * .5,
self.base_selbox[2] * .5, self.base_selbox[3] * .5, self.base_selbox[4] * .5,
self.base_selbox[3] * .5, self.base_selbox[5] * .5, self.base_selbox[6] * .5}
self.base_selbox[4] * .5,
self.base_selbox[5] * .5,
self.base_selbox[6] * .5
}
end end
if self.health == 0 then if self.health == 0 then
@ -3075,15 +3133,14 @@ function mob_class:mob_activate(staticdata, def, dtime)
self.path.following = false -- currently following path? self.path.following = false -- currently following path?
self.path.stuck_timer = 0 -- if stuck for too long search for path self.path.stuck_timer = 0 -- if stuck for too long search for path
-- Armor groups -- Armor groups (immortal = 1 for custom damage handling)
-- immortal=1 because we use custom health
-- handling (using "health" property)
local armor local armor
if type(self.armor) == "table" then if type(self.armor) == "table" then
armor = table.copy(self.armor) armor = table.copy(self.armor)
armor.immortal = 1 -- armor.immortal = 1
else else
armor = {immortal = 1, fleshy = self.armor} -- armor = {immortal = 1, fleshy = self.armor}
armor = {fleshy = self.armor}
end end
self.object:set_armor_groups(armor) self.object:set_armor_groups(armor)
@ -3166,7 +3223,7 @@ function mob_class:mob_expire(pos, dtime)
effect(pos, 15, "tnt_smoke.png", 2, 4, 2, 0) effect(pos, 15, "tnt_smoke.png", 2, 4, 2, 0)
self.object:remove() remove_mob(self, true)
return return
end end
@ -3175,7 +3232,23 @@ end
-- main mob function -- main mob function
function mob_class:on_step(dtime) function mob_class:on_step(dtime, moveresult)
--[[ moveresult contains this for physical mobs
{
touching_ground = boolean,
collides = boolean,
standing_on_object = boolean,
collisions = {
{
type = string, -- "node" or "object",
axis = string, -- "x", "y" or "z"
node_pos = vector, -- if type is "node"
object = ObjectRef, -- if type is "object"
old_velocity = vector,
new_velocity = vector,
}}
}]]
if use_cmi then if use_cmi then
cmi.notify_step(self.object, dtime) cmi.notify_step(self.object, dtime)
@ -3204,11 +3277,12 @@ function mob_class:on_step(dtime)
self.standing_in = node_ok({ self.standing_in = node_ok({
x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name
--print ("standing in " .. self.standing_in) --print("standing in " .. self.standing_in)
-- if standing inside solid block then jump to escape -- if standing inside solid block then jump to escape
if minetest.registered_nodes[self.standing_in].walkable and if minetest.registered_nodes[self.standing_in].walkable
minetest.registered_nodes[self.standing_in].drawtype == "normal" then and minetest.registered_nodes[self.standing_in].drawtype
== "normal" then
self.object:set_velocity({ self.object:set_velocity({
x = 0, x = 0,
@ -3224,7 +3298,7 @@ function mob_class:on_step(dtime)
self:set_velocity(0) self:set_velocity(0)
end end
-- check for mob expiration (0.25 instead of dtime since were in a timer) -- has mob expired (0.25 instead of dtime since were in a timer)
self:mob_expire(pos, 0.25) self:mob_expire(pos, 0.25)
end end
@ -3234,7 +3308,6 @@ function mob_class:on_step(dtime)
end end
-- smooth rotation by ThomasMonroe314 -- smooth rotation by ThomasMonroe314
if self.delay and self.delay > 0 then if self.delay and self.delay > 0 then
if self.delay == 1 then if self.delay == 1 then
@ -3269,8 +3342,6 @@ function mob_class:on_step(dtime)
self.object:set_yaw(yaw) self.object:set_yaw(yaw)
end end
-- end rotation
-- knockback timer -- knockback timer
if self.pause_timer > 0 then if self.pause_timer > 0 then
@ -3344,7 +3415,7 @@ end
-- default function when mobs are blown up with TNT -- default function when mobs are blown up with TNT
function mob_class:on_blast(damage) function mob_class:on_blast(damage)
--print ("----- Damage", damage) --print("-- blast damage", damage)
self.object:punch(self.object, 1.0, { self.object:punch(self.object, 1.0, {
full_punch_interval = 1.0, full_punch_interval = 1.0,
@ -3378,7 +3449,7 @@ minetest.register_entity(name, setmetatable({
do_custom = def.do_custom, do_custom = def.do_custom,
jump_height = def.jump_height, jump_height = def.jump_height,
drawtype = def.drawtype, -- DEPRECATED, use rotate instead drawtype = def.drawtype, -- DEPRECATED, use rotate instead
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2 rotate = math.rad(def.rotate or 0), -- 0=front 90=side 180=back 270=side2
glow = def.glow, glow = def.glow,
lifetimer = def.lifetimer, lifetimer = def.lifetimer,
hp_min = max(1, (def.hp_min or 5) * difficulty), hp_min = max(1, (def.hp_min or 5) * difficulty),
@ -3514,7 +3585,7 @@ end
function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) interval, chance, aoc, min_height, max_height, day_toggle, on_spawn)
-- Do mobs spawn at all? -- Do mobs spawn at all?
if not mobs_spawn then if not mobs_spawn then
@ -3550,12 +3621,19 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
chance = max(1, (chance * mob_chance_multiplier)), chance = max(1, (chance * mob_chance_multiplier)),
catch_up = false, catch_up = false,
action = function(pos, node, active_object_count, active_object_count_wider) action = function(pos, node, active_object_count,
active_object_count_wider)
-- is mob actually registered? -- is mob actually registered?
if not mobs.spawning_mobs[name] if not mobs.spawning_mobs[name]
or not minetest.registered_entities[name] then or not minetest.registered_entities[name] then
--print ("--- mob doesn't exist", name) --print("--- mob doesn't exist", name)
return
end
-- are we over active mob limit
if active_mobs >= active_limit then
--print("--- active mob limit reached", active_mobs, active_limit)
return return
end end
@ -3574,12 +3652,12 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
local num_mob, is_pla = count_mobs(pos, name) local num_mob, is_pla = count_mobs(pos, name)
if not is_pla then if not is_pla then
--print ("--- no players within active area, will not spawn " .. name) --print("--- no players within active area, will not spawn " .. name)
return return
end end
if num_mob >= aoc then if num_mob >= aoc then
--print ("--- too many " .. name .. " in area", num_mob .. "/" .. aoc) --print("--- too many " .. name .. " in area", num_mob .. "/" .. aoc)
return return
end end
@ -3591,13 +3669,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
if tod > 4500 and tod < 19500 then if tod > 4500 and tod < 19500 then
-- daylight, but mob wants night -- daylight, but mob wants night
if day_toggle == false then if day_toggle == false then
--print ("--- mob needs night", name) --print("--- mob needs night", name)
return return
end end
else else
-- night time but mob wants day -- night time but mob wants day
if day_toggle == true then if day_toggle == true then
--print ("--- mob needs day", name) --print("--- mob needs day", name)
return return
end end
end end
@ -3609,7 +3687,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
-- are we spawning within height limits? -- are we spawning within height limits?
if pos.y > max_height if pos.y > max_height
or pos.y < min_height then or pos.y < min_height then
--print ("--- height limits not met", name, pos.y) --print("--- height limits not met", name, pos.y)
return return
end end
@ -3618,14 +3696,14 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
if not light if not light
or light > max_light or light > max_light
or light < min_light then or light < min_light then
--print ("--- light limits not met", name, light) --print("--- light limits not met", name, light)
return return
end end
-- mobs cannot spawn in protected areas when enabled -- mobs cannot spawn in protected areas when enabled
if not spawn_protected if not spawn_protected
and minetest.is_protected(pos, "") then and minetest.is_protected(pos, "") then
--print ("--- inside protected area", name) --print("--- inside protected area", name)
return return
end end
@ -3636,7 +3714,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
for n = 1, #objs do for n = 1, #objs do
if objs[n]:is_player() then if objs[n]:is_player() then
--print ("--- player too close", name) --print("--- player too close", name)
return return
end end
end end
@ -3681,9 +3759,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
if minetest.registered_nodes[ if minetest.registered_nodes[
node_ok(pos2).name].walkable == true then node_ok(pos2).name].walkable == true then
--print("--- not enough space to spawn", name)
--print ("--- not enough space to spawn", name)
return return
end end
end end
@ -3703,11 +3779,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light,
end end
local mob = minetest.add_entity(pos, name) local mob = minetest.add_entity(pos, name)
--[[
print ("[mobs] Spawned " .. name .. " at " -- active_mobs = active_mobs + 1
.. minetest.pos_to_string(pos) .. " on "
.. node.name .. " near " .. neighbors[1]) -- print("[mobs] Spawned " .. name .. " at "
]] -- .. minetest.pos_to_string(pos) .. " on "
-- .. node.name .. " near " .. neighbors[1])
if on_spawn then if on_spawn then
local ent = mob:get_luaentity() local ent = mob:get_luaentity()
@ -3764,7 +3842,7 @@ function mobs:register_arrow(name, def)
hit_mob = def.hit_mob, hit_mob = def.hit_mob,
hit_object = def.hit_object, hit_object = def.hit_object,
drop = def.drop or false, -- drops arrow as registered item when true drop = def.drop or false, -- drops arrow as registered item when true
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0}, collisionbox = def.collisionbox or {-.1, -.1, -.1, .1, .1, .1},
timer = 0, timer = 0,
switch = 0, switch = 0,
owner_id = def.owner_id, owner_id = def.owner_id,
@ -3784,18 +3862,15 @@ function mobs:register_arrow(name, def)
local pos = self.object:get_pos() local pos = self.object:get_pos()
if self.switch == 0 if self.switch == 0 or self.timer > 150 then
or self.timer > 150 then
self.object:remove() ; -- print ("removed arrow") self.object:remove() ; -- print("removed arrow")
return return
end end
-- does arrow have a tail (fireball) -- does arrow have a tail (fireball)
if def.tail if def.tail and def.tail == 1 and def.tail_texture then
and def.tail == 1
and def.tail_texture then
minetest.add_particle({ minetest.add_particle({
pos = pos, pos = pos,
@ -3827,21 +3902,23 @@ function mobs:register_arrow(name, def)
self.object:get_luaentity().name) self.object:get_luaentity().name)
end end
self.object:remove() ; -- print ("hit node") self.object:remove() ; -- print("hit node")
return return
end end
end end
if self.hit_player or self.hit_mob then if self.hit_player or self.hit_mob or self.hit_object then
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do for _,player in pairs(
minetest.get_objects_inside_radius(pos, 1.0)) do
if self.hit_player if self.hit_player and player:is_player() then
and player:is_player() then
self:hit_player(player) self:hit_player(player)
self.object:remove() ; -- print ("hit player")
self.object:remove() ; -- print("hit player")
return return
end end
@ -3855,7 +3932,7 @@ function mobs:register_arrow(name, def)
self:hit_mob(player) self:hit_mob(player)
self.object:remove() ; --print ("hit mob") self.object:remove() ; --print("hit mob")
return return
end end
@ -3868,7 +3945,7 @@ function mobs:register_arrow(name, def)
self:hit_object(player) self:hit_object(player)
self.object:remove(); -- print ("hit object") self.object:remove(); -- print("hit object")
return return
end end
@ -3883,10 +3960,7 @@ end
-- compatibility function -- compatibility function
function mobs:explosion(pos, radius) function mobs:explosion(pos, radius)
mobs:boom({sounds = {explode = "tnt_explode"}}, pos, radius)
local self = {sounds = {explode = "tnt_explode"}}
mobs:boom(self, pos, radius)
end end
@ -3957,10 +4031,12 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
local pos = pointed_thing.above local pos = pointed_thing.above
-- am I clicking on something with existing on_rightclick function? -- does existing on_rightclick function exist?
local under = minetest.get_node(pointed_thing.under) local under = minetest.get_node(pointed_thing.under)
local def = minetest.registered_nodes[under.name] local def = minetest.registered_nodes[under.name]
if def and def.on_rightclick then if def and def.on_rightclick then
return def.on_rightclick( return def.on_rightclick(
pointed_thing.under, under, placer, itemstack) pointed_thing.under, under, placer, itemstack)
end end
@ -3980,6 +4056,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
if not ent then return end -- sanity check if not ent then return end -- sanity check
-- active_mobs = active_mobs + 1
-- set owner if not a monster -- set owner if not a monster
if ent.type ~= "monster" then if ent.type ~= "monster" then
ent.owner = placer:get_player_name() ent.owner = placer:get_player_name()
@ -4006,10 +4084,12 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
local pos = pointed_thing.above local pos = pointed_thing.above
-- am I clicking on something with existing on_rightclick function? -- does existing on_rightclick function exist?
local under = minetest.get_node(pointed_thing.under) local under = minetest.get_node(pointed_thing.under)
local def = minetest.registered_nodes[under.name] local def = minetest.registered_nodes[under.name]
if def and def.on_rightclick then if def and def.on_rightclick then
return def.on_rightclick( return def.on_rightclick(
pointed_thing.under, under, placer, itemstack) pointed_thing.under, under, placer, itemstack)
end end
@ -4021,6 +4101,15 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
return return
end end
-- have we reached active mob limit
if active_mobs >= active_limit then
minetest.chat_send_player(placer:get_player_name(),
S("Active Mob Limit Reached!")
.. " (" .. active_mobs
.. " / " .. active_limit .. ")")
return
end
pos.y = pos.y + 1 pos.y = pos.y + 1
local mob = minetest.add_entity(pos, mob) local mob = minetest.add_entity(pos, mob)
@ -4028,6 +4117,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative)
if not ent then return end -- sanity check if not ent then return end -- sanity check
-- active_mobs = active_mobs + 1
-- don't set owner if monster or sneak pressed -- don't set owner if monster or sneak pressed
if ent.type ~= "monster" if ent.type ~= "monster"
and not placer:get_player_control().sneak then and not placer:get_player_control().sneak then
@ -4057,10 +4148,10 @@ function mobs:force_capture(self, clicker)
local tmp = {} local tmp = {}
for _,stat in pairs(self) do for _,stat in pairs(self) do
local t = type(stat) local t = type(stat)
if t ~= "function"
and t ~= "nil" if t ~= "function" and t ~= "nil" and t ~= "userdata" then
and t ~= "userdata" then
tmp[_] = self[_] tmp[_] = self[_]
end end
end end
@ -4077,15 +4168,15 @@ function mobs:force_capture(self, clicker)
minetest.add_item(clicker:get_pos(), new_stack) minetest.add_item(clicker:get_pos(), new_stack)
end end
self.object:remove()
self:mob_sound("default_place_node_hard") self:mob_sound("default_place_node_hard")
remove_mob(self, true)
end end
-- capture critter (thanks to blert2112 for idea) -- capture critter (thanks to blert2112 for idea)
function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, function mobs:capture_mob(self, clicker, chance_hand, chance_net,
force_take, replacewith) chance_lasso, force_take, replacewith)
if self.child if self.child
or not clicker:is_player() or not clicker:is_player()
@ -4112,8 +4203,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso,
end end
-- is mob tamed? -- is mob tamed?
if self.tamed == false if self.tamed == false and force_take == false then
and force_take == false then
minetest.chat_send_player(name, S("Not tamed!")) minetest.chat_send_player(name, S("Not tamed!"))
@ -4121,8 +4211,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso,
end end
-- cannot pick up if not owner -- cannot pick up if not owner
if self.owner ~= name if self.owner ~= name and force_take == false then
and force_take == false then
minetest.chat_send_player(name, S("@1 is owner!", self.owner)) minetest.chat_send_player(name, S("@1 is owner!", self.owner))
@ -4170,7 +4259,9 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso,
local tmp = {} local tmp = {}
for _,stat in pairs(self) do for _,stat in pairs(self) do
local t = type(stat) local t = type(stat)
if t ~= "function" if t ~= "function"
and t ~= "nil" and t ~= "nil"
and t ~= "userdata" then and t ~= "userdata" then
@ -4191,7 +4282,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso,
minetest.add_item(clicker:get_pos(), new_stack) minetest.add_item(clicker:get_pos(), new_stack)
end end
self.object:remove() remove_mob(self, true)
self:mob_sound("default_place_node_hard") self:mob_sound("default_place_node_hard")
@ -4199,12 +4290,14 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso,
-- when chance above fails or set to 0, miss! -- when chance above fails or set to 0, miss!
elseif chance and chance ~= 0 then elseif chance and chance ~= 0 then
minetest.chat_send_player(name, S("Missed!")) minetest.chat_send_player(name, S("Missed!"))
self:mob_sound("mobs_swing") self:mob_sound("mobs_swing")
return false return false
-- when chance set to nil always return a miss (used for npc walk/follow) -- when chance is nil always return a miss (used for npc walk/follow)
elseif not chance then elseif not chance then
return false return false
end end
@ -4244,7 +4337,8 @@ function mobs:protect(self, clicker)
local pos = self.object:get_pos() local pos = self.object:get_pos()
pos.y = pos.y + self.collisionbox[2] + 0.5 pos.y = pos.y + self.collisionbox[2] + 0.5
effect(self.object:get_pos(), 25, "mobs_protect_particle.png", 0.5, 4, 2, 15) effect(self.object:get_pos(), 25, "mobs_protect_particle.png",
0.5, 4, 2, 15)
self:mob_sound("mobs_spell") self:mob_sound("mobs_spell")
@ -4348,9 +4442,11 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
local tag = self.nametag or "" local tag = self.nametag or ""
minetest.show_formspec(name, "mobs_nametag", "size[8,4]" minetest.show_formspec(name, "mobs_nametag",
"size[8,4]"
.. "field[0.5,1;7.5,0;name;" .. "field[0.5,1;7.5,0;name;"
.. minetest.formspec_escape(S("Enter name:")) .. ";" .. tag .. "]" .. minetest.formspec_escape(S("Enter name:"))
.. ";" .. tag .. "]"
.. "button_exit[2.5,3.5;3,1;mob_rename;" .. "button_exit[2.5,3.5;3,1;mob_rename;"
.. minetest.formspec_escape(S("Rename")) .. "]") .. minetest.formspec_escape(S("Rename")) .. "]")
end end
@ -4425,10 +4521,14 @@ function mobs:alias_mob(old_name, new_name)
on_activate = function(self, staticdata) on_activate = function(self, staticdata)
if minetest.registered_entities[new_name] then if minetest.registered_entities[new_name] then
minetest.add_entity(self.object:get_pos(), new_name, staticdata)
minetest.add_entity(self.object:get_pos(),
new_name, staticdata)
else
-- active_mobs = active_mobs - 1
end end
self.object:remove() remove_mob(self)
end, end,
get_staticdata = function(self) get_staticdata = function(self)