1
0
mirror of https://codeberg.org/tenplus1/mobs_redo.git synced 2025-07-20 01:00:22 +02:00

Compare commits

...

21 Commits

Author SHA1 Message Date
deee28fc59 added 'arrow_override' function to mob definition 2021-08-01 18:58:21 +01:00
ab44080ff8 do not despawn tamed monsters 2021-07-31 08:35:53 +01:00
9f46182bb4 remove need for default mod 2021-07-22 09:48:56 +01:00
2535b5636e add support for MarkBu's pathfinder mod 2021-07-22 09:34:21 +01:00
2d014a75c4 tweak "stand" mobs velocity 2021-07-21 19:38:41 +01:00
db3831dccf update api.txt 2021-07-14 15:53:20 +01:00
29b2204f7c add 'mobs_spawn_monster_protected' switch (thanks 0siribix) 2021-07-14 15:43:02 +01:00
7fbfd9d59c fix "stand" mobs fall jitter 2021-07-13 16:56:53 +01:00
e50d04a6aa fix facing fence jump bug 2021-06-14 09:30:03 +01:00
33589ebda0 improve pathfinding level 2 digging/building, add infotext, stop mob attack spin, tweak & tidy code 2021-06-13 09:39:59 +01:00
6670938c3d add mob height check for glitching through blocks 2021-06-11 22:25:00 +01:00
f131806008 add 'mob_smooth_rotate' setting 2021-06-01 14:17:25 +01:00
49325c9c8d fix weapon sound selection 2021-05-27 15:42:21 +01:00
8012250258 nil check for mount 2021-05-21 17:46:10 +01:00
ec44aa91c4 add 'damage_group' and 'damage_texture_modifier' settings to mob definition 2021-05-15 09:33:35 +01:00
f58841ab3e show current texture for mob reset stick 2021-05-10 14:57:21 +01:00
350fc3647c add extra settings to arrow definition 2021-05-04 08:02:43 +01:00
70c68f6ebc change so only players can push mobs 2021-04-18 09:05:16 +01:00
c023ecc36f reduce shapeless crafts 2021-04-13 21:09:34 +01:00
9be934ec25 make it possible to use advanced protector on normally protected mob 2021-04-13 12:41:09 +01:00
f6e16a5503 return actual damage position (created particle py), check for nil damage groups 2021-04-11 17:23:22 +01:00
8 changed files with 313 additions and 255 deletions

412
api.lua
View File

@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi")
mobs = {
mod = "redo",
version = "20210407",
version = "20210801",
intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {}
}
@ -28,8 +28,7 @@ local rad = math.rad
local atann = math.atan
local atan = function(x)
if not x or x ~= x then
--error("atan bassed NaN")
return 0
return 0 -- NaN
else
return atann(x)
end
@ -57,6 +56,7 @@ local disable_blood = settings:get_bool("mobs_disable_blood")
local mobs_drop_items = settings:get_bool("mobs_drop_items") ~= false
local mobs_griefing = settings:get_bool("mobs_griefing") ~= false
local spawn_protected = settings:get_bool("mobs_spawn_protected") ~= false
local spawn_monster_protected = settings:get_bool("mobs_spawn_monster_protected") ~= false
local remove_far = settings:get_bool("remove_far_mobs") ~= false
local mob_area_spawn = settings:get_bool("mob_area_spawn")
local difficulty = tonumber(settings:get("mob_difficulty")) or 1.0
@ -66,9 +66,9 @@ local mob_nospawn_range = tonumber(settings:get("mob_nospawn_range") or 12)
local active_limit = tonumber(settings:get("mob_active_limit") or 0)
local mob_chance_multiplier = tonumber(settings:get("mob_chance_multiplier") or 1)
local peaceful_player_enabled = settings:get_bool("enable_peaceful_player")
local mob_smooth_rotate = settings:get_bool("mob_smooth_rotate") ~= false
local active_mobs = 0
-- Peaceful mode message so players will know there are no monsters
if peaceful_only then
minetest.register_on_joinplayer(function(player)
@ -83,7 +83,7 @@ local aoc_range = tonumber(settings:get("active_block_range")) * 16
-- pathfinding settings
local enable_pathfinding = true
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 = 5 -- how long will mob follow path before giving up
-- default nodes
local node_fire = "fire:basic_flame"
@ -224,10 +224,7 @@ function mob_class:collision()
for _,object in ipairs(minetest.get_objects_inside_radius(pos, width)) do
if object:is_player()
or (object:get_luaentity()
and object:get_luaentity()._cmi_is_mob == true
and object ~= self.object) then
if object:is_player() then
local pos2 = object:get_pos()
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
@ -277,7 +274,9 @@ function mob_class:set_velocity(v)
-- halt mob if it has been ordered to stay
if self.order == "stand" then
self.object:set_velocity({x = 0, y = 0, z = 0})
local vel = self.object:get_velocity() or {y = 0}
self.object:set_velocity({x = 0, y = vel.y, z = 0})
return
end
@ -339,10 +338,12 @@ function mob_class:set_yaw(yaw, delay)
yaw = 0
end
delay = delay or 0
delay = mob_smooth_rotate and (delay or 0) or 0
if delay == 0 then
self.object:set_yaw(yaw)
return yaw
end
@ -404,7 +405,6 @@ function mob_class:set_animation(anim, force)
0, self.animation[anim .. "_loop"] ~= false)
end
-- above function exported for mount.lua
function mobs:set_animation(entity, anim)
entity.set_animation(entity, anim)
end
@ -591,7 +591,7 @@ function mob_class:attempt_flight_correction(override)
local escape_direction = vdirection(pos, escape_target)
self.object:set_velocity(
vmultiply(escape_direction, 1)) --self.run_velocity))
vmultiply(escape_direction, 1))
return true
end
@ -643,7 +643,7 @@ function mobs:yaw_to_pos(self, target, rot)
end
-- if stay near set then check periodically for nodes and turn towards them
-- if stay near set then periodically check for nodes and turn towards them
function mob_class:do_stay_near()
if not self.stay_near then return false end
@ -740,9 +740,15 @@ function mob_class:update_tag()
col = "#FF0000"
end
-- build infotext
self.infotext = "Health: " .. self.health .. " / " .. self.hp_max
.. "\n" .. "Owner: " .. self.owner
-- set changes
self.object:set_properties({
nametag = self.nametag,
nametag_color = col
nametag_color = col,
infotext = self.infotext
})
end
@ -790,10 +796,7 @@ function mob_class:item_drop()
end
-- only drop rare items (drops.min = 0) if killed by player
if death_by_player then
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
elseif self.drops[n].min ~= 0 then
if death_by_player or self.drops[n].min ~= 0 then
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
end
@ -868,18 +871,17 @@ function mob_class:check_for_death(cmi_cause)
end
-- backup nametag so we can show health stats
if not self.nametag2 then
self.nametag2 = self.nametag or ""
end
-- if not self.nametag2 then
-- self.nametag2 = self.nametag or ""
-- end
if show_health
and (cmi_cause and cmi_cause.type == "punch") then
self.htimer = 2
self.nametag = "" .. self.health .. " / " .. self.hp_max
-- if show_health
-- and (cmi_cause and cmi_cause.type == "punch") then
-- self.htimer = 2
-- self.nametag = "♥ " .. self.health .. " / " .. self.hp_max
self:update_tag()
end
-- end
return false
end
@ -1049,13 +1051,13 @@ function mob_class:do_env_damage()
end
-- reset nametag after showing health stats
if self.htimer < 1 and self.nametag2 then
-- if self.htimer < 1 and self.nametag2 then
self.nametag = self.nametag2
self.nametag2 = nil
-- self.nametag = self.nametag2
-- self.nametag2 = nil
self:update_tag()
end
-- end
local pos = self.object:get_pos() ; if not pos then return end
@ -1070,17 +1072,20 @@ function mob_class:do_env_damage()
end
-- particle appears at random mob height
pos.y = pos.y + random(self.collisionbox[2], self.collisionbox[5])
local py = {
x = pos.x,
y = pos.y + random(self.collisionbox[2], self.collisionbox[5]),
z = pos.z
}
local nodef = minetest.registered_nodes[self.standing_in]
-- water
if self.water_damage ~= 0
and nodef.groups.water then
if self.water_damage ~= 0 and nodef.groups.water then
self.health = self.health - self.water_damage
effect(pos, 5, "bubble.png", nil, nil, 1, nil)
effect(py, 5, "bubble.png", nil, nil, 1, nil)
if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then
@ -1088,12 +1093,11 @@ function mob_class:do_env_damage()
end
-- lava damage
elseif self.lava_damage ~= 0
and nodef.groups.lava then
elseif self.lava_damage ~= 0 and nodef.groups.lava then
self.health = self.health - self.lava_damage
effect(pos, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true)
effect(py, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true)
if self:check_for_death({type = "environment", pos = pos,
node = self.standing_in, hot = true}) then
@ -1101,12 +1105,11 @@ function mob_class:do_env_damage()
end
-- fire damage
elseif self.fire_damage ~= 0
and nodef.groups.fire then
elseif self.fire_damage ~= 0 and nodef.groups.fire then
self.health = self.health - self.fire_damage
effect(pos, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true)
effect(py, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true)
if self:check_for_death({type = "environment", pos = pos,
node = self.standing_in, hot = true}) then
@ -1115,11 +1118,11 @@ function mob_class:do_env_damage()
-- damage_per_second node check (not fire and lava)
elseif nodef.damage_per_second ~= 0
and nodef.groups.lava == 0 and nodef.groups.fire == 0 then
and nodef.groups.lava == nil and nodef.groups.fire == nil then
self.health = self.health - nodef.damage_per_second
effect(pos, 5, "tnt_smoke.png")
effect(py, 5, "tnt_smoke.png")
if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then
@ -1132,7 +1135,7 @@ function mob_class:do_env_damage()
self.health = self.health - self.air_damage
effect(pos, 3, "bubble.png", 1, 1, 1, 0.2)
effect(py, 3, "bubble.png", 1, 1, 1, 0.2)
if self:check_for_death({type = "environment",
pos = pos, node = self.standing_in}) then
@ -1150,7 +1153,7 @@ function mob_class:do_env_damage()
self.health = self.health - self.light_damage
effect(pos, 5, "tnt_smoke.png")
effect(py, 5, "tnt_smoke.png")
if self:check_for_death({type = "light"}) then
return true
@ -1235,19 +1238,21 @@ function mob_class:do_jump()
local blocked = minetest.registered_nodes[nodt.name].walkable
--print("standing on:", self.standing_on, pos.y - 0.25)
--print("in front:", nod.name, pos.y + 0.5)
--print("in front above:", nodt.name, pos.y + 1.5)
-- jump if standing on solid node (not snow) and not blocked above
if (self.walk_chance == 0
or minetest.registered_items[nod.name].walkable)
and not blocked
and nod.name ~= node_snow then
if not nod.name:find("fence")
and not nod.name:find("gate")
and not nod.name:find("wall") then
-- are we facing a fence or wall
if nod.name:find("fence") or nod.name:find("gate") or nod.name:find("wall") then
self.facing_fence = true
end
--[[
print("on: " .. self.standing_on
.. ", front: " .. nod.name
.. ", front above: " .. nodt.name
.. ", blocked: " .. (blocked and "yes" or "no")
.. ", fence: " .. (self.facing_fence and "yes" or "no")
)
]]
-- jump if standing on solid node (not snow) and not blocked
if (self.walk_chance == 0 or minetest.registered_items[nod.name].walkable)
and not blocked and not self.facing_fence and nod.name ~= node_snow then
local v = self.object:get_velocity()
@ -1274,19 +1279,17 @@ function mob_class:do_jump()
self:mob_sound(self.sounds.jump)
end
self.jump_count = 0
return true
else
self.facing_fence = true
end
end
-- if blocked against a block/wall for 5 counts then turn
if not self.following
and (self.facing_fence or blocked) then
-- if blocked for 3 counts then turn
if not self.following and (self.facing_fence or blocked) then
self.jump_count = (self.jump_count or 0) + 1
if self.jump_count > 4 then
if self.jump_count > 2 then
local yaw = self.object:get_yaw() or 0
local turn = random(0, 2) + 1.35
@ -1357,7 +1360,7 @@ end
-- Thanks Wuzzy for the following editable settings
local HORNY_TIME = 30
local HORNY_AGAIN_TIME = 300
local HORNY_AGAIN_TIME = 60 * 5 -- 5 minutes
local CHILD_GROW_TIME = 60 * 20 -- 20 minutes
-- find two animals of same type and breed if nearby and horny
@ -1385,16 +1388,15 @@ function mob_class:breed()
if self.on_grown then
self.on_grown(self)
else
-- jump when fully grown so as not to fall into ground
-- self.object:set_velocity({
-- x = 0,
-- y = self.jump_height,
-- z = 0
-- })
local pos = self.object:get_pos() ; if not pos then return end
local ent = self.object:get_luaentity()
pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4
self.object:set_pos(pos)
-- jump slightly when fully grown so as not to fall into ground
self.object:set_velocity({x = 0, y = 0.5, z = 0 })
end
end
@ -1617,7 +1619,43 @@ end
local los_switcher = false
local height_switcher = false
local can_dig_drop = function(pos)
if minetest.is_protected(pos, "") then
return false
end
local node = node_ok(pos, "air").name
local ndef = minetest.registered_nodes[node]
if node ~= "ignore"
and ndef
and ndef.drawtype ~= "airlike"
and not ndef.groups.level
and not ndef.groups.unbreakable
and not ndef.groups.liquid then
local drops = minetest.get_node_drops(node)
for _, item in ipairs(drops) do
minetest.add_item({
x = pos.x - 0.5 + random(),
y = pos.y - 0.5 + random(),
z = pos.z - 0.5 + random()
}, item)
end
minetest.remove_node(pos)
return true
end
return false
end
local pathfinder_mod = minetest.get_modpath("pathfinder")
-- path finding and smart mob routine by rnd,
-- line_of_sight and other edits by Elkien3
function mob_class:smart_mobs(s, p, dist, dtime)
@ -1746,13 +1784,18 @@ function mob_class:smart_mobs(s, p, dist, dtime)
jumpheight = 1
end
if pathfinder_mod then
self.path.way = pathfinder.find_path(s, p1, self, dtime)
else
self.path.way = minetest.find_path(s, p1, 16, jumpheight,
dropheight, "Dijkstra")
end
--[[
-- show path using particles
if self.path.way and #self.path.way > 0 then
print("-- path length:" .. tonumber(#self.path.way))
for _,pos in pairs(self.path.way) do
minetest.add_particle({
pos = pos,
@ -1782,8 +1825,8 @@ function mob_class:smart_mobs(s, p, dist, dtime)
-- lets make way by digging/building if not accessible
if self.pathfinding == 2 and mobs_griefing then
-- is player higher than mob?
if s.y < p1.y then
-- is player more than 1 block higher than mob?
if p1.y > (s.y + 1) then
-- build upwards
if not minetest.is_protected(s, "") then
@ -1791,7 +1834,6 @@ function mob_class:smart_mobs(s, p, dist, dtime)
local ndef1 = minetest.registered_nodes[self.standing_in]
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
minetest.set_node(s, {name = mobs.fallback_node})
end
end
@ -1802,27 +1844,19 @@ function mob_class:smart_mobs(s, p, dist, dtime)
s.y = s.y + sheight
-- remove one block above to make room to jump
if not minetest.is_protected(s, "") then
local node1 = node_ok(s, "air").name
local ndef1 = minetest.registered_nodes[node1]
if node1 ~= "air"
and node1 ~= "ignore"
and ndef1
and not ndef1.groups.level
and not ndef1.groups.unbreakable
and not ndef1.groups.liquid then
minetest.set_node(s, {name = "air"})
minetest.add_item(s, ItemStack(node1))
end
end
can_dig_drop(s)
s.y = s.y - sheight
self.object:set_pos({x = s.x, y = s.y + 2, z = s.z})
-- is player more than 1 block lower than mob
elseif p1.y < (s.y - 1) then
-- dig down
s.y = s.y - self.collisionbox[4] - 0.2
can_dig_drop(s)
else -- dig 2 blocks to make door toward player direction
local yaw1 = self.object:get_yaw() + pi / 2
@ -1832,37 +1866,12 @@ function mob_class:smart_mobs(s, p, dist, dtime)
z = s.z + sin(yaw1)
}
if not minetest.is_protected(p1, "") then
local node1 = node_ok(p1, "air").name
local ndef1 = minetest.registered_nodes[node1]
if node1 ~= "air"
and node1 ~= "ignore"
and ndef1
and not ndef1.groups.level
and not ndef1.groups.unbreakable
and not ndef1.groups.liquid then
minetest.add_item(p1, ItemStack(node1))
minetest.set_node(p1, {name = "air"})
end
-- dig bottom node first incase of door
can_dig_drop(p1)
p1.y = p1.y + 1
node1 = node_ok(p1, "air").name
ndef1 = minetest.registered_nodes[node1]
if node1 ~= "air"
and node1 ~= "ignore"
and ndef1
and not ndef1.groups.level
and not ndef1.groups.unbreakable
and not ndef1.groups.liquid then
minetest.add_item(p1, ItemStack(node1))
minetest.set_node(p1, {name = "air"})
end
end
can_dig_drop(p1)
end
end
@ -2594,7 +2603,10 @@ function mob_class:do_states(dtime)
self:smart_mobs(s, p, dist, dtime)
end
if self.at_cliff then
-- distance padding to stop spinning mob
local pad = abs(p.x - s.x) + abs(p.z - s.z)
if self.at_cliff or pad < 0.2 then
self:set_velocity(0)
self:set_animation("stand")
@ -2612,7 +2624,6 @@ function mob_class:do_states(dtime)
self:set_animation("walk")
end
end
else -- rnd: if inside reach range
self.path.stuck = false
@ -2648,9 +2659,11 @@ function mob_class:do_states(dtime)
self.attack = attached
end
local dgroup = self.damage_group or "fleshy"
self.attack:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = {fleshy = self.damage}
damage_groups = {[dgroup] = self.damage}
}, nil)
end
end
@ -2690,6 +2703,12 @@ function mob_class:do_states(dtime)
local obj = minetest.add_entity(p, self.arrow)
local ent = obj:get_luaentity()
local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5
-- check for custom override for arrow
if self.arrow_override then
self.arrow_override(ent)
end
local v = ent.velocity or 1 -- or set to default
ent.switch = 1
@ -2757,11 +2776,7 @@ function mob_class:falling(pos)
end
-- fall at set speed
self.object:set_acceleration({
x = 0,
y = fall_speed,
z = 0
})
self.object:set_acceleration({x = 0, y = fall_speed, z = 0})
end
@ -2784,8 +2799,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- error checking when mod profiling is enabled
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 true
end
@ -2861,6 +2877,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
if self.immune_to[n][1] == weapon_def.name then
damage = self.immune_to[n][2] or 0
break
-- if "all" then no tools deal damage unless it's specified in list
@ -2873,13 +2890,14 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- healing
if damage <= -1 then
self.health = self.health - floor(damage)
return true
end
if use_cmi
and cmi.notify_punch(
self.object, hitter, tflp, tool_capabilities, dir, damage) then
and cmi.notify_punch(self.object, hitter, tflp, tool_capabilities, dir, damage) then
return true
end
@ -2898,10 +2916,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
end
end
if tr then
if weapon_def.original_description then
if tr and weapon_def.original_description then
toolranks.new_afteruse(weapon, hitter, nil, {wear = wear})
end
else
weapon:add_wear(wear)
end
@ -2911,21 +2927,11 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- only play hit sound and show blood effects if damage is 1 or over
if damage >= 1 then
-- weapon sounds
if weapon_def.sounds then
-- select tool use sound if found, or fallback to default
local snd = weapon_def.sound and weapon_def.sound.use
or "default_punch"
local s = random(0, #weapon_def.sounds)
minetest.sound_play(weapon_def.sounds[s], {
object = self.object,
max_hear_distance = 8
}, true)
else
minetest.sound_play("default_punch", {
object = self.object,
max_hear_distance = 5
}, true)
end
minetest.sound_play(snd, {object = self.object, max_hear_distance = 8}, true)
-- blood_particles
if not disable_blood and self.blood_amount > 0 then
@ -2934,8 +2940,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
local blood = self.blood_texture
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 :)
if damage > 10 then
@ -2948,7 +2953,6 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
end
effect(pos, amount, blood, 1, 2, 1.75, nil, nil, true)
end
-- do damage
@ -2958,29 +2962,13 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
local hot = tool_capabilities and tool_capabilities.damage_groups
and tool_capabilities.damage_groups.fire
if self:check_for_death({type = "punch",
puncher = hitter, hot = hot}) then
if self:check_for_death({type = "punch", puncher = hitter, hot = hot}) then
return true
end
--[[ add healthy afterglow when hit (causes lag with large textures)
minetest.after(0.1, function()
if not self.object:get_luaentity() then return end
self.object:set_texture_mod("^[colorize:#c9900070")
minetest.after(0.3, function()
if not self.object:get_luaentity() then return end
self.object:set_texture_mod(self.texture_mods)
end)
end) ]]
end -- END if damage
-- knock back effect (only on full punch)
if self.knock_back
and tflp >= punch_interval then
if self.knock_back and tflp >= punch_interval then
local v = self.object:get_velocity()
@ -3002,11 +2990,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- use tool knockback value or default
kb = tool_capabilities.damage_groups["knockback"] or kb
self.object:set_velocity({
x = dir.x * kb,
y = up,
z = dir.z * kb
})
self.object:set_velocity({x = dir.x * kb, y = up, z = dir.z * kb})
self.pause_timer = 0.25
end
@ -3253,10 +3237,8 @@ function mob_class:mob_activate(staticdata, def, dtime)
local armor
if type(self.armor) == "table" then
armor = table_copy(self.armor)
-- armor.immortal = 1
else
-- armor = {immortal = 1, fleshy = self.armor}
armor = {fleshy = self.armor}
armor = {fleshy = self.armor} -- immortal = 1
end
self.object:set_armor_groups(armor)
@ -3287,7 +3269,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
self.object:set_texture_mod(self.texture_mods)
-- set 5.x flag to remove monsters when map area unloaded
if remove_far and self.type == "monster" then
if remove_far and self.type == "monster" and not self.tamed then
self.static_save = false
end
@ -3354,23 +3336,7 @@ end
-- main mob function
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 self.state == "die" then return end ----------------
if self.state == "die" then return end
if use_cmi then
cmi.notify_step(self.object, dtime)
@ -3559,6 +3525,13 @@ function mobs:register_mob(name, def)
mobs.spawning_mobs[name] = {}
local collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}
-- quick fix to stop mobs glitching through nodes if too small
if -collisionbox[2] + collisionbox[5] < 1.01 then
collisionbox[5] = collisionbox[2] + 0.99
end
minetest.register_entity(name, setmetatable({
stepheight = def.stepheight,
@ -3580,8 +3553,8 @@ minetest.register_entity(name, setmetatable({
lifetimer = def.lifetimer,
hp_min = max(1, (def.hp_min or 5) * difficulty),
hp_max = max(1, (def.hp_max or 10) * difficulty),
collisionbox = def.collisionbox,
selectionbox = def.selectionbox or def.collisionbox,
collisionbox = collisionbox, --def.collisionbox,
selectionbox = def.selectionbox or collisionbox, --def.collisionbox,
visual = def.visual,
visual_size = def.visual_size,
mesh = def.mesh,
@ -3590,6 +3563,8 @@ minetest.register_entity(name, setmetatable({
walk_velocity = def.walk_velocity,
run_velocity = def.run_velocity,
damage = max(0, (def.damage or 0) * difficulty),
damage_group = def.damage_group,
damage_texture_modifier = def.damage_texture_modifier,
light_damage = def.light_damage,
light_damage_min = def.light_damage_min,
light_damage_max = def.light_damage_max,
@ -3604,6 +3579,7 @@ minetest.register_entity(name, setmetatable({
armor = def.armor,
on_rightclick = def.on_rightclick,
arrow = def.arrow,
arrow_override = def.arrow_override,
shoot_interval = def.shoot_interval,
sounds = def.sounds,
animation = def.animation,
@ -3887,11 +3863,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
local numbers = settings:get(name)
if numbers then
numbers = numbers:split(",")
chance = tonumber(numbers[1]) or chance
aoc = tonumber(numbers[2]) or aoc
if chance == 0 then
minetest.log("warning",
string.format("[mobs] %s has spawning disabled", name))
return
@ -3995,8 +3973,10 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
return
end
-- mobs cannot spawn in protected areas when enabled
if not spawn_protected
-- check if mob can spawn inside protected areas
if (spawn_protected == false
or (spawn_monster_protected == false
and minetest.registered_entities[name].type == "monster"))
and minetest.is_protected(pos, "") then
--print("--- inside protected area", name)
return
@ -4123,7 +4103,10 @@ function mobs:register_arrow(name, def)
minetest.register_entity(name, {
physical = false,
physical = def.physical or false,
collide_with_objects = def.collide_with_objects or false,
static_save = false,
visual = def.visual,
visual_size = def.visual_size,
textures = def.textures,
@ -4282,7 +4265,7 @@ function mobs:boom(self, pos, radius)
radius = radius,
damage_radius = radius,
sound = self.sounds and self.sounds.explode,
explode_center = true,
explode_center = true
})
else
mobs:safe_boom(self, pos, radius)
@ -4614,7 +4597,8 @@ function mobs:protect(self, clicker)
return true
end
if self.protected then
if (self.protected and tool_name == "mobs:protector")
or (self.protected == 2 and tool_name == "mobs:protector2") then
minetest.chat_send_player(name, S("Already protected!"))
return true
end
@ -4671,14 +4655,14 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
self.health = self.hp_max
if self.htimer < 1 then
-- if self.htimer < 1 then
minetest.chat_send_player(clicker:get_player_name(),
S("@1 at full health (@2)",
self.name:split(":")[2], tostring(self.health)))
-- minetest.chat_send_player(clicker:get_player_name(),
-- S("@1 at full health (@2)",
-- self.name:split(":")[2], tostring(self.health)))
self.htimer = 5
end
-- self.htimer = 5
-- end
end
self.object:set_hp(self.health)
@ -4716,6 +4700,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
end
self.tamed = true
self.static_save = true
if not self.owner or self.owner == "" then
self.owner = clicker:get_player_name()
@ -4820,14 +4805,13 @@ function mobs:alias_mob(old_name, new_name)
-- entity
minetest.register_entity(":" .. old_name, {
physical = false,
physical = false, static_save = false,
on_activate = function(self, staticdata)
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)
end
remove_mob(self)

View File

@ -56,6 +56,8 @@ functions needed for the mob to work properly which contains the following:
'view_range' how many nodes in distance the mob can see a player.
'damage' how many health points the mob does to a player or another
mob when melee attacking.
'damage_group' group in which damage is dealt, dedaults to "fleshy".
'damage_texture_modifier' applies texture modifier on hit e.g "^[brighten"
'knock_back' when true has mobs falling backwards when hit, the greater
the damage the more they move back.
'fear_height' is how high a cliff or edge has to be before the mob stops
@ -121,6 +123,8 @@ functions needed for the mob to work properly which contains the following:
continue chasing.
'arrow' holds the pre-defined arrow object to shoot when
attacking.
'arrow_override' function that allows tweaking of arrow entity from
inside mob definition (self) passed to function.
'dogshoot_switch' allows switching between attack types by using timers
(1 for shoot, 2 for dogfight)
'dogshoot_count_max' contains how many seconds before switching from
@ -465,6 +469,8 @@ This function registers a arrow for mobs with the attack type shoot.
'visual' same is in minetest.register_entity()
'visual_size' same is in minetest.register_entity()
'textures' same is in minetest.register_entity()
'physical' same is in minetest.register_entity() [default: false]
'collide_with_objects' same as above
'velocity' the velocity of the arrow
'drop' if set to true any arrows hitting a node will drop as item
'hit_player' a function that is called when the arrow hits a player;
@ -695,6 +701,8 @@ External Settings for "minetest.conf"
is false)
'mobs_spawn_protected' if set to false then mobs will not spawn in protected
areas (default is true)
'mobs_spawn_monster_protected' if set to false then monsters will not spawn in
protected areas (default is true)
'remove_far_mobs' if true then untamed mobs that are outside players
visual range will be removed (default is true)
'mobname' can change specific mob chance rate (0 to disable) and
@ -718,6 +726,7 @@ External Settings for "minetest.conf"
'mob_area_spawn' When true will check surrounding area the size of the
mob for obstructions before spawning, otherwise it
defaults to checking the height of the mob only.
'mob_smooth_rotate' Enables smooth rotation when mobs turn by default.
Players can override the spawn chance for each mob registered by adding a line
to their minetest.conf file with a new value, the lower the value the more each

View File

@ -10,9 +10,8 @@ minetest.register_craftitem("mobs:nametag", {
if minetest.get_modpath("dye") and minetest.get_modpath("farming") then
minetest.register_craft({
type = "shapeless",
output = "mobs:nametag",
recipe = {"default:paper", "dye:black", "farming:string"}
recipe = {{"default:paper", "dye:black", "farming:string"}}
})
end
@ -149,7 +148,7 @@ minetest.register_craft({
-- make sure we can register fences
if default.register_fence then
if minetest.get_modpath("default") and default.register_fence then
-- mob fence (looks like normal fence but collision is 2 high)
default.register_fence("mobs:fence_wood", {
@ -165,6 +164,7 @@ default.register_fence("mobs:fence_wood", {
}
}
})
end
-- mob fence top (has enlarged collisionbox to stop mobs getting over)
minetest.register_node("mobs:fence_top", {
@ -197,8 +197,6 @@ minetest.register_craft({
}
})
end
-- items that can be used as fuel
minetest.register_craft({
@ -299,12 +297,18 @@ minetest.register_tool(":mobs:mob_reset_stick", {
tex_obj = obj
-- get base texture
local bt = tex_obj:get_luaentity().base_texture[1]
if type(bt) ~= "string" then
bt = ""
end
local name = user:get_player_name()
local tex = ""
minetest.show_formspec(name, "mobs_texture", "size[8,4]"
.. "field[0.5,1;7.5,0;name;"
.. minetest.formspec_escape(S("Enter texture:")) .. ";" .. tex .. "]"
.. minetest.formspec_escape(S("Enter texture:")) .. ";" .. bt .. "]"
.. "button_exit[2.5,3.5;3,1;mob_texture_change;"
.. minetest.formspec_escape(S("Change")) .. "]")
end
@ -355,17 +359,17 @@ minetest.register_node("mobs:meatblock", {
tiles = {"mobs_meat_top.png", "mobs_meat_bottom.png", "mobs_meat_side.png"},
paramtype2 = "facedir",
groups = {choppy = 1, oddly_breakable_by_hand = 1, flammable = 2},
sounds = default.node_sound_leaves_defaults(),
sounds = default and default.node_sound_leaves_defaults(),
on_place = minetest.rotate_node,
on_use = minetest.item_eat(20),
on_use = minetest.item_eat(20)
})
minetest.register_craft({
output = "mobs:meatblock",
type = "shapeless",
-- type = "shapeless",
recipe = {
"group:food_meat", "group:food_meat", "group:food_meat",
"group:food_meat", "group:food_meat", "group:food_meat",
"group:food_meat", "group:food_meat", "group:food_meat"
{"group:food_meat", "group:food_meat", "group:food_meat"},
{"group:food_meat", "group:food_meat", "group:food_meat"},
{"group:food_meat", "group:food_meat", "group:food_meat"}
}
})

View File

@ -1,4 +1,4 @@
default
default?
tnt?
dye?
farming?
@ -7,3 +7,4 @@ intllib?
lucky_block?
cmi?
toolranks?
pathfinder?

View File

@ -1,4 +1,4 @@
name = mobs
depends = default
optional_depends = tnt, dye, farming, invisibility, intllib, lucky_block, cmi, toolranks
depends =
optional_depends = default, tnt, dye, farming, invisibility, intllib, lucky_block, cmi, toolranks, pathfinder
description = Adds a mob api for mods to add animals or monsters etc.

View File

@ -22,6 +22,7 @@ end
local function node_is(pos)
local node = node_ok(pos)
if node.name == "air" then
@ -69,6 +70,7 @@ end
local function force_detach(player)
local attached_to = player:get_attach()
if not attached_to then
@ -97,7 +99,9 @@ minetest.register_on_leaveplayer(function(player)
end)
minetest.register_on_shutdown(function()
local players = minetest.get_connected_players()
for i = 1, #players do
force_detach(players[i])
end
@ -112,6 +116,7 @@ end)
-- Just for correct detaching
local function find_free_pos(pos)
local check = {
{x = 1, y = 0, z = 0},
{x = 1, y = 1, z = 0},
@ -124,10 +129,14 @@ local function find_free_pos(pos)
}
for _, c in pairs(check) do
local npos = {x = pos.x + c.x, y = pos.y + c.y, z = pos.z + c.z}
local node = minetest.get_node_or_nil(npos)
if node and node.name then
local def = minetest.registered_nodes[node.name]
if def and not def.walkable and
def.liquidtype == "none" then
return npos
@ -141,6 +150,7 @@ end
-------------------------------------------------------------------------------
function mobs.attach(entity, player)
entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
@ -154,6 +164,7 @@ function mobs.attach(entity, player)
local attach_at = entity.driver_attach_at
local eye_offset = entity.driver_eye_offset
entity.driver = player
force_detach(player)
@ -170,6 +181,7 @@ function mobs.attach(entity, player)
})
minetest.after(0.2, function()
if player and player:is_player() then
player_api.set_animation(player, "sit", 30)
end
@ -183,9 +195,13 @@ function mobs.detach(player)
force_detach(player)
minetest.after(0.1, function()
if player and player:is_player() then
local pos = find_free_pos(player:get_pos())
pos.y = pos.y + 0.5
player:set_pos(pos)
end
end)
@ -193,8 +209,8 @@ end
function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
local yaw = entity.object:get_yaw() or 0
local yaw = entity.object:get_yaw() or 0
local rot_view = 0
if entity.player_rotation.y == 90 then
@ -208,14 +224,17 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
-- process controls
if entity.driver then
local ctrl = entity.driver:get_player_control()
-- move forwards
if ctrl.up then
entity.v = entity.v + entity.accel / 10
-- move backwards
elseif ctrl.down then
if entity.max_speed_reverse == 0 and entity.v == 0 then
return
end
@ -225,7 +244,9 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
-- mob rotation
local horz
if entity.alt_turn == true then
horz = yaw
if ctrl.left then
@ -243,21 +264,29 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
if can_fly then
-- fly up
if ctrl.jump then
velo.y = velo.y + 1
if velo.y > entity.accel then velo.y = entity.accel end
elseif velo.y > 0 then
velo.y = velo.y - 0.1
if velo.y < 0 then velo.y = 0 end
end
-- fly down
if ctrl.sneak then
velo.y = velo.y - 1
if velo.y < -entity.accel then velo.y = -entity.accel end
elseif velo.y < 0 then
velo.y = velo.y + 0.1
if velo.y > 0 then velo.y = 0 end
end
else
@ -274,6 +303,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
-- if not moving then set animation and return
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
if stand_anim then
mobs:set_animation(entity, stand_anim)
end
@ -292,8 +322,10 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
entity.v = entity.v - 0.02 * s
if s ~= get_sign(entity.v) then
entity.object:set_velocity({x = 0, y = 0, z = 0})
entity.v = 0
return
end
@ -310,6 +342,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
-- Set position, velocity and acceleration
local p = entity.object:get_pos()
if not p then return end
local new_acce = {x = 0, y = -9.81, z = 0}
@ -320,18 +353,23 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
local v = entity.v
if ni == "air" then
if can_fly == true then
new_acce.y = 0
end
elseif ni == "liquid" or ni == "lava" then
if ni == "lava" and entity.lava_damage ~= 0 then
entity.lava_counter = (entity.lava_counter or 0) + dtime
if entity.lava_counter > 1 then
minetest.sound_play("default_punch", {
object = entity.object,
max_hear_distance = 5
})
}, true)
entity.object:punch(entity.object, 1.0, {
full_punch_interval = 1.0,
@ -343,11 +381,14 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end
local terrain_type = entity.terrain_type
if terrain_type == 2 or terrain_type == 3 then
new_acce.y = 0
p.y = p.y + 1
if node_is(p) == "liquid" then
if velo.y >= 5 then
velo.y = 5
elseif velo.y < 0 then
@ -357,7 +398,9 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end
else
if abs(velo.y) < 1 then
local pos = entity.object:get_pos()
if not pos then return end
pos.y = floor(pos.y) + 0.5
@ -371,6 +414,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
end
local new_velo = get_velocity(v, yaw - rot_view, velo.y)
new_acce.y = new_acce.y + acce_y
entity.object:set_velocity(new_velo)
@ -382,11 +426,14 @@ end
-- directional flying routine by D00Med (edited by TenPlus1)
function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
local ctrl = entity.driver:get_player_control()
local velo = entity.object:get_velocity()
local dir = entity.driver:get_look_dir()
local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
if not ctrl or not velo then return end
if ctrl.up then
entity.object:set_velocity({
x = dir.x * speed,
@ -395,6 +442,7 @@ function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
})
elseif ctrl.down then
entity.object:set_velocity({
x = -dir.x * speed,
y = dir.y * speed + 2,
@ -409,6 +457,7 @@ function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
-- firing arrows
if ctrl.LMB and ctrl.sneak and shoots then
local pos = entity.object:get_pos()
local obj = minetest.add_entity({
x = pos.x + 0 + dir.x * 2.5,
@ -416,11 +465,15 @@ function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
z = pos.z + 0 + dir.z * 2.5}, arrow)
local ent = obj:get_luaentity()
if ent then
ent.switch = 1 -- for mob specific arrows
ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
yaw = entity.driver:get_look_horizontal()
obj:set_yaw(yaw + pi / 2)
obj:set_velocity(vec)
else

View File

@ -23,7 +23,8 @@ Lucky Blocks: 9
Changelog:
- 1.55 - Add 'peaceful_player' privelage and setting so mobs don't attack specific players (thanks sfence)
- 1.56 - Added arrow_override function to mob definition to tweak arrow entity settings, tamed monsters no longer despawn when outside loaded map area.
- 1.55 - Add 'peaceful_player' privelage and setting so mobs don't attack specific players (thanks sfence), add support for MarkBu's pathfinder mod, remove need for default mod
- 1.54 - Simplified animal breeding function, added editable settings (thanks Wuzzy), Child mobs now take 20 mins to grow up, reverted to simple mob spawning with setting to use area checks, on_flop added, air_damage added.
- 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded.
- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game,

View File

@ -13,6 +13,9 @@ mobs_griefing (Griefing Mobs) bool true
# If false then Mobs no longer spawn inside player protected areas
mobs_spawn_protected (Spawn Mobs in protected areas) bool true
# If false then Monsters no longer spawn inside player protected areas
mobs_spawn_monster_protected (Spawn Monsters in protected areas) bool true
# If true Mobs will be removed once a map chunk is out of view
remove_far_mobs (Remove far Mobs) bool true
@ -39,3 +42,6 @@ mob_area_spawn (Mob Area Spawn) bool false
# Enable peaceful player attack prevention
enable_peaceful_player (Mobs do not attack peaceful player without reason) bool false
# Enable mobs smooth rotation
mob_smooth_rotate (Smooth rotation for mobs) bool true