improve pathfinding level 2 digging/building, add infotext, stop mob attack spin, tweak & tidy code

master
TenPlus1 2021-06-13 09:39:59 +01:00
parent 6670938c3d
commit 33589ebda0
1 changed files with 148 additions and 193 deletions

341
api.lua
View File

@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi")
mobs = { mobs = {
mod = "redo", mod = "redo",
version = "20210610", version = "20210613",
intllib = S, intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {} invis = minetest.global_exists("invisibility") and invisibility or {}
} }
@ -28,8 +28,7 @@ local rad = math.rad
local atann = math.atan local atann = math.atan
local atan = function(x) local atan = function(x)
if not x or x ~= x then if not x or x ~= x then
--error("atan bassed NaN") return 0 -- NaN
return 0
else else
return atann(x) return atann(x)
end end
@ -225,9 +224,6 @@ function mob_class:collision()
for _,object in ipairs(minetest.get_objects_inside_radius(pos, width)) do for _,object in ipairs(minetest.get_objects_inside_radius(pos, width)) do
if object:is_player() then if object:is_player() then
-- or (object:get_luaentity()
-- and object:get_luaentity()._cmi_is_mob == true
-- and object ~= self.object) then
local pos2 = object:get_pos() local pos2 = object:get_pos()
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z} local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
@ -406,7 +402,6 @@ function mob_class:set_animation(anim, force)
0, self.animation[anim .. "_loop"] ~= false) 0, self.animation[anim .. "_loop"] ~= false)
end end
-- above function exported for mount.lua
function mobs:set_animation(entity, anim) function mobs:set_animation(entity, anim)
entity.set_animation(entity, anim) entity.set_animation(entity, anim)
end end
@ -593,7 +588,7 @@ function mob_class:attempt_flight_correction(override)
local escape_direction = vdirection(pos, escape_target) local escape_direction = vdirection(pos, escape_target)
self.object:set_velocity( self.object:set_velocity(
vmultiply(escape_direction, 1)) --self.run_velocity)) vmultiply(escape_direction, 1))
return true return true
end end
@ -645,7 +640,7 @@ function mobs:yaw_to_pos(self, target, rot)
end 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() function mob_class:do_stay_near()
if not self.stay_near then return false end if not self.stay_near then return false end
@ -742,9 +737,15 @@ function mob_class:update_tag()
col = "#FF0000" col = "#FF0000"
end end
-- build infotext
self.infotext = "Health: " .. self.health .. " / " .. self.hp_max
.. "\n" .. "Owner: " .. self.owner
-- set changes
self.object:set_properties({ self.object:set_properties({
nametag = self.nametag, nametag = self.nametag,
nametag_color = col nametag_color = col,
infotext = self.infotext
}) })
end end
@ -792,10 +793,7 @@ function mob_class:item_drop()
end end
-- only drop rare items (drops.min = 0) if killed by player -- only drop rare items (drops.min = 0) if killed by player
if death_by_player then if death_by_player or self.drops[n].min ~= 0 then
obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
elseif self.drops[n].min ~= 0 then
obj = minetest.add_item(pos, ItemStack(item .. " " .. num)) obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
end end
@ -870,18 +868,17 @@ function mob_class:check_for_death(cmi_cause)
end end
-- backup nametag so we can show health stats -- backup nametag so we can show health stats
if not self.nametag2 then -- if not self.nametag2 then
self.nametag2 = self.nametag or "" -- self.nametag2 = self.nametag or ""
end -- end
if show_health -- if show_health
and (cmi_cause and cmi_cause.type == "punch") then -- and (cmi_cause and cmi_cause.type == "punch") then
self.htimer = 2
self.nametag = "" .. self.health .. " / " .. self.hp_max
-- self.htimer = 2
-- self.nametag = "♥ " .. self.health .. " / " .. self.hp_max
self:update_tag() self:update_tag()
end -- end
return false return false
end end
@ -1051,13 +1048,13 @@ function mob_class:do_env_damage()
end end
-- reset nametag after showing health stats -- 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.nametag = self.nametag2
self.nametag2 = nil -- self.nametag2 = nil
self:update_tag() self:update_tag()
end -- end
local pos = self.object:get_pos() ; if not pos then return end local pos = self.object:get_pos() ; if not pos then return end
@ -1081,8 +1078,7 @@ function mob_class:do_env_damage()
local nodef = minetest.registered_nodes[self.standing_in] local nodef = minetest.registered_nodes[self.standing_in]
-- water -- water
if self.water_damage ~= 0 if self.water_damage ~= 0 and nodef.groups.water then
and nodef.groups.water then
self.health = self.health - self.water_damage self.health = self.health - self.water_damage
@ -1094,8 +1090,7 @@ function mob_class:do_env_damage()
end end
-- lava damage -- lava damage
elseif self.lava_damage ~= 0 elseif self.lava_damage ~= 0 and nodef.groups.lava then
and nodef.groups.lava then
self.health = self.health - self.lava_damage self.health = self.health - self.lava_damage
@ -1107,8 +1102,7 @@ function mob_class:do_env_damage()
end end
-- fire damage -- fire damage
elseif self.fire_damage ~= 0 elseif self.fire_damage ~= 0 and nodef.groups.fire then
and nodef.groups.fire then
self.health = self.health - self.fire_damage self.health = self.health - self.fire_damage
@ -1245,54 +1239,51 @@ function mob_class:do_jump()
--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 -- are we facing a fence or wall
if (self.walk_chance == 0 if nod.name:find("fence") or nod.name:find("gate") or nod.name:find("wall") then
or minetest.registered_items[nod.name].walkable) self.facing_fence = true
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
local v = self.object:get_velocity()
v.y = self.jump_height
self:set_animation("jump") -- only when defined
self.object:set_velocity(v)
-- when in air move forward
minetest.after(0.3, function(self, v)
if self.object:get_luaentity() then
self.object:set_acceleration({
x = v.x * 2,
y = 0,
z = v.z * 2
})
end
end, self, v)
if self:get_velocity() > 0 then
self:mob_sound(self.sounds.jump)
end
return true
else
self.facing_fence = true
end
end end
-- if blocked against a block/wall for 5 counts then turn -- jump if standing on solid node (not snow) and not blocked above
if not self.following if (self.walk_chance == 0 or minetest.registered_items[nod.name].walkable)
and (self.facing_fence or blocked) then and not blocked and nod.name ~= node_snow then
local v = self.object:get_velocity()
v.y = self.jump_height
self:set_animation("jump") -- only when defined
self.object:set_velocity(v)
-- when in air move forward
minetest.after(0.3, function(self, v)
if self.object:get_luaentity() then
self.object:set_acceleration({
x = v.x * 2,
y = 0,
z = v.z * 2
})
end
end, self, v)
if self:get_velocity() > 0 then
self:mob_sound(self.sounds.jump)
end
self.jump_count = 0
return true
end
-- 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 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 yaw = self.object:get_yaw() or 0
local turn = random(0, 2) + 1.35 local turn = random(0, 2) + 1.35
@ -1363,7 +1354,7 @@ end
-- Thanks Wuzzy for the following editable settings -- Thanks Wuzzy for the following editable settings
local HORNY_TIME = 30 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 local CHILD_GROW_TIME = 60 * 20 -- 20 minutes
-- find two animals of same type and breed if nearby and horny -- find two animals of same type and breed if nearby and horny
@ -1391,16 +1382,15 @@ function mob_class:breed()
if self.on_grown then if self.on_grown then
self.on_grown(self) self.on_grown(self)
else 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 pos = self.object:get_pos() ; if not pos then return end
local ent = self.object:get_luaentity() local ent = self.object:get_luaentity()
pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4 pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4
self.object:set_pos(pos) 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
end end
@ -1623,6 +1613,40 @@ end
local los_switcher = false local los_switcher = false
local height_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
-- path finding and smart mob routine by rnd, -- path finding and smart mob routine by rnd,
-- line_of_sight and other edits by Elkien3 -- line_of_sight and other edits by Elkien3
@ -1788,8 +1812,8 @@ function mob_class:smart_mobs(s, p, dist, dtime)
-- lets make way by digging/building if not accessible -- lets make way by digging/building if not accessible
if self.pathfinding == 2 and mobs_griefing then if self.pathfinding == 2 and mobs_griefing then
-- is player higher than mob? -- is player more than 1 block higher than mob?
if s.y < p1.y then if p1.y > (s.y + 1) then
-- build upwards -- build upwards
if not minetest.is_protected(s, "") then if not minetest.is_protected(s, "") then
@ -1797,8 +1821,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
local ndef1 = minetest.registered_nodes[self.standing_in] local ndef1 = minetest.registered_nodes[self.standing_in]
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
minetest.set_node(s, {name = mobs.fallback_node})
minetest.set_node(s, {name = mobs.fallback_node})
end end
end end
@ -1808,27 +1831,19 @@ function mob_class:smart_mobs(s, p, dist, dtime)
s.y = s.y + sheight s.y = s.y + sheight
-- remove one block above to make room to jump -- remove one block above to make room to jump
if not minetest.is_protected(s, "") then can_dig_drop(s)
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
s.y = s.y - sheight s.y = s.y - sheight
self.object:set_pos({x = s.x, y = s.y + 2, z = s.z}) 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 else -- dig 2 blocks to make door toward player direction
local yaw1 = self.object:get_yaw() + pi / 2 local yaw1 = self.object:get_yaw() + pi / 2
@ -1838,37 +1853,12 @@ function mob_class:smart_mobs(s, p, dist, dtime)
z = s.z + sin(yaw1) z = s.z + sin(yaw1)
} }
if not minetest.is_protected(p1, "") then -- dig bottom node first incase of door
can_dig_drop(p1)
local node1 = node_ok(p1, "air").name p1.y = p1.y + 1
local ndef1 = minetest.registered_nodes[node1]
if node1 ~= "air" can_dig_drop(p1)
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
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
end end
end end
@ -2600,7 +2590,10 @@ function mob_class:do_states(dtime)
self:smart_mobs(s, p, dist, dtime) self:smart_mobs(s, p, dist, dtime)
end 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_velocity(0)
self:set_animation("stand") self:set_animation("stand")
@ -2618,7 +2611,6 @@ function mob_class:do_states(dtime)
self:set_animation("walk") self:set_animation("walk")
end end
end end
else -- rnd: if inside reach range else -- rnd: if inside reach range
self.path.stuck = false self.path.stuck = false
@ -2765,11 +2757,7 @@ function mob_class:falling(pos)
end end
-- fall at set speed -- fall at set speed
self.object:set_acceleration({ self.object:set_acceleration({x = 0, y = fall_speed, z = 0})
x = 0,
y = fall_speed,
z = 0
})
end end
@ -2792,8 +2780,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- 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 true return true
end end
@ -2869,6 +2858,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
if self.immune_to[n][1] == weapon_def.name then if self.immune_to[n][1] == weapon_def.name then
damage = self.immune_to[n][2] or 0 damage = self.immune_to[n][2] or 0
break break
-- if "all" then no tools deal damage unless it's specified in list -- if "all" then no tools deal damage unless it's specified in list
@ -2881,13 +2871,14 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- healing -- healing
if damage <= -1 then if damage <= -1 then
self.health = self.health - floor(damage) self.health = self.health - floor(damage)
return true return true
end end
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
return true return true
end end
@ -2906,10 +2897,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
end end
end end
if tr then if tr and weapon_def.original_description then
if weapon_def.original_description then toolranks.new_afteruse(weapon, hitter, nil, {wear = wear})
toolranks.new_afteruse(weapon, hitter, nil, {wear = wear})
end
else else
weapon:add_wear(wear) weapon:add_wear(wear)
end end
@ -2957,20 +2946,6 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
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 return true
end 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 end -- END if damage
-- knock back effect (only on full punch) -- knock back effect (only on full punch)
@ -2996,11 +2971,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
-- use tool knockback value or default -- use tool knockback value or default
kb = tool_capabilities.damage_groups["knockback"] or kb kb = tool_capabilities.damage_groups["knockback"] or kb
self.object:set_velocity({ self.object:set_velocity({x = dir.x * kb, y = up, z = dir.z * kb})
x = dir.x * kb,
y = up,
z = dir.z * kb
})
self.pause_timer = 0.25 self.pause_timer = 0.25
end end
@ -3247,10 +3218,8 @@ function mob_class:mob_activate(staticdata, def, dtime)
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
else else
-- armor = {immortal = 1, fleshy = self.armor} armor = {fleshy = self.armor} -- immortal = 1
armor = {fleshy = self.armor}
end end
self.object:set_armor_groups(armor) self.object:set_armor_groups(armor)
@ -3348,23 +3317,7 @@ end
-- main mob function -- main mob function
function mob_class:on_step(dtime, moveresult) function mob_class:on_step(dtime, moveresult)
--[[ moveresult contains this for physical mobs if self.state == "die" then return end
{
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 use_cmi then if use_cmi then
cmi.notify_step(self.object, dtime) cmi.notify_step(self.object, dtime)
@ -3890,11 +3843,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
local numbers = settings:get(name) local numbers = settings:get(name)
if numbers then if numbers then
numbers = numbers:split(",") numbers = numbers:split(",")
chance = tonumber(numbers[1]) or chance chance = tonumber(numbers[1]) or chance
aoc = tonumber(numbers[2]) or aoc aoc = tonumber(numbers[2]) or aoc
if chance == 0 then if chance == 0 then
minetest.log("warning", minetest.log("warning",
string.format("[mobs] %s has spawning disabled", name)) string.format("[mobs] %s has spawning disabled", name))
return return
@ -4678,14 +4633,14 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame)
self.health = self.hp_max self.health = self.hp_max
if self.htimer < 1 then -- if self.htimer < 1 then
minetest.chat_send_player(clicker:get_player_name(), -- minetest.chat_send_player(clicker:get_player_name(),
S("@1 at full health (@2)", -- S("@1 at full health (@2)",
self.name:split(":")[2], tostring(self.health))) -- self.name:split(":")[2], tostring(self.health)))
self.htimer = 5 -- self.htimer = 5
end -- end
end end
self.object:set_hp(self.health) self.object:set_hp(self.health)