code cleanups
This commit is contained in:
parent
8ad5d16ccc
commit
40492282ae
|
@ -1,29 +1,24 @@
|
|||
local mob_class = mcl_mobs.mob_class
|
||||
local mob_class_meta = {__index = mcl_mobs.mob_class}
|
||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
-- API for Mobs Redo: VoxeLibre Edition
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
local CRASH_WARN_FREQUENCY = 60
|
||||
local LIFETIMER_DISTANCE = 47
|
||||
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
|
||||
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90
|
||||
-- 30927 seems to be the edge of the world, so could be closer, but this is safer
|
||||
|
||||
-- Localize
|
||||
local S = minetest.get_translator("mcl_mobs")
|
||||
|
||||
local DEVELOPMENT = minetest.settings:get_bool("mcl_development",false)
|
||||
|
||||
-- Invisibility mod check
|
||||
mcl_mobs.invis = {}
|
||||
|
||||
local remove_far = true
|
||||
|
||||
local mobs_debug = minetest.settings:get_bool("mobs_debug", false) -- Shows helpful debug info above each mob
|
||||
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
|
||||
|
||||
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
|
||||
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90
|
||||
-- 30927 seems to be the edge of the world, so could be closer, but this is safer
|
||||
|
||||
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", true)
|
||||
local DEVELOPMENT = minetest.settings:get_bool("mcl_development", false)
|
||||
|
||||
-- Peaceful mode message so players will know there are no monsters
|
||||
if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
||||
|
@ -36,10 +31,7 @@ end
|
|||
function mob_class:update_tag() --update nametag and/or the debug box
|
||||
local tag
|
||||
if mobs_debug then
|
||||
local name = self.name
|
||||
if self.nametag and self.nametag ~= "" then
|
||||
name = self.nametag
|
||||
end
|
||||
local name = self.nametag ~= "" and self.nametag or self.name
|
||||
tag = "name = '"..tostring(name).."'\n"..
|
||||
"state = '"..tostring(self.state).."'\n"..
|
||||
"order = '"..tostring(self.order).."'\n"..
|
||||
|
@ -56,9 +48,7 @@ function mob_class:update_tag() --update nametag and/or the debug box
|
|||
else
|
||||
tag = self.nametag
|
||||
end
|
||||
self.object:set_properties({
|
||||
nametag = tag,
|
||||
})
|
||||
self.object:set_properties({ nametag = tag })
|
||||
end
|
||||
|
||||
function mob_class:jock_to(mob, reletive_pos, rot)
|
||||
|
@ -74,19 +64,15 @@ function mob_class:jock_to(mob, reletive_pos, rot)
|
|||
end
|
||||
|
||||
function mob_class:get_staticdata()
|
||||
|
||||
for _,p in pairs(minetest.get_connected_players()) do
|
||||
self:remove_particlespawners(p:get_player_name())
|
||||
end
|
||||
|
||||
-- remove mob when out of range unless tamed
|
||||
if remove_far
|
||||
and self:despawn_allowed()
|
||||
and self.lifetimer <= 20 then
|
||||
if remove_far and self:despawn_allowed() and self.lifetimer <= 20 then
|
||||
if spawn_logging then
|
||||
minetest.log("action", "[mcl_mobs] Mob "..tostring(self.name).." despawns at "..minetest.pos_to_string(vector.round(self.object:get_pos())) .. " - out of range")
|
||||
end
|
||||
|
||||
return "remove"-- nil
|
||||
end
|
||||
|
||||
|
@ -95,17 +81,9 @@ function mob_class:get_staticdata()
|
|||
self.state = "stand"
|
||||
|
||||
local tmp = {}
|
||||
|
||||
for tag, stat in pairs(self) do
|
||||
|
||||
local t = type(stat)
|
||||
|
||||
if t ~= "function"
|
||||
and t ~= "nil"
|
||||
and t ~= "userdata"
|
||||
and tag ~= "_cmi_components" then
|
||||
tmp[tag] = self[tag]
|
||||
end
|
||||
if t ~= "function" and t ~= "nil" and t ~= "userdata" and tag ~= "_cmi_components" then tmp[tag] = self[tag] end
|
||||
end
|
||||
|
||||
tmp._mcl_potions = self._mcl_potions
|
||||
|
@ -120,10 +98,7 @@ function mob_class:get_staticdata()
|
|||
end
|
||||
|
||||
local function valid_texture(self, def_textures)
|
||||
if not self.base_texture then
|
||||
return false
|
||||
end
|
||||
|
||||
if not self.base_texture then return false end
|
||||
if self.texture_selected then
|
||||
if #def_textures < self.texture_selected then
|
||||
self.texture_selected = nil
|
||||
|
@ -148,32 +123,18 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
end
|
||||
|
||||
local tmp = minetest.deserialize(staticdata)
|
||||
|
||||
if tmp then
|
||||
-- Patch incorrectly converted mobs
|
||||
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then
|
||||
mcl_mobs.strip_staticdata(tmp)
|
||||
end
|
||||
|
||||
for _,stat in pairs(tmp) do
|
||||
self[_] = stat
|
||||
end
|
||||
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then mcl_mobs.strip_staticdata(tmp) end
|
||||
for _, stat in pairs(tmp) do self[_] = stat end
|
||||
end
|
||||
|
||||
--If textures in definition change, reload textures
|
||||
if not valid_texture(self, def.textures) then
|
||||
|
||||
-- compatiblity with old simple mobs textures
|
||||
if type(def.textures[1]) == "string" then
|
||||
def.textures = {def.textures}
|
||||
end
|
||||
|
||||
if not self.texture_selected then
|
||||
local c = 1
|
||||
if #def.textures > c then c = #def.textures end
|
||||
self.texture_selected = math.random(c)
|
||||
end
|
||||
if type(def.textures[1]) == "string" then def.textures = {def.textures} end
|
||||
|
||||
self.texture_selected = self.texture_selected or math.random(#def.textures)
|
||||
self.base_texture = def.textures[self.texture_selected]
|
||||
self.base_mesh = def.mesh
|
||||
self.base_size = self.visual_size
|
||||
|
@ -181,9 +142,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.base_selbox = self.selectionbox
|
||||
end
|
||||
|
||||
if not self.base_selbox then
|
||||
self.base_selbox = self.selectionbox or self.base_colbox
|
||||
end
|
||||
self.base_selbox = self.base_selbox or self.selectionbox or self.base_colbox
|
||||
|
||||
local textures = self.base_texture
|
||||
local mesh = self.base_mesh
|
||||
|
@ -191,26 +150,11 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
local colbox = self.base_colbox
|
||||
local selbox = self.base_selbox
|
||||
|
||||
if self.gotten == true
|
||||
and def.gotten_texture then
|
||||
textures = def.gotten_texture
|
||||
end
|
||||
|
||||
if self.gotten == true
|
||||
and def.gotten_mesh then
|
||||
mesh = def.gotten_mesh
|
||||
end
|
||||
|
||||
if self.gotten == true and def.gotten_texture then textures = def.gotten_texture end
|
||||
if self.gotten == true and def.gotten_mesh then mesh = def.gotten_mesh end
|
||||
if self.child == true then
|
||||
|
||||
vis_size = {
|
||||
x = self.base_size.x * .5,
|
||||
y = self.base_size.y * .5,
|
||||
}
|
||||
|
||||
if def.child_texture then
|
||||
textures = def.child_texture[1]
|
||||
end
|
||||
vis_size = { x = self.base_size.x * .5, y = self.base_size.y * .5 }
|
||||
if def.child_texture then textures = def.child_texture[1] end
|
||||
|
||||
colbox = {
|
||||
self.base_colbox[1] * .5,
|
||||
|
@ -230,16 +174,12 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
}
|
||||
end
|
||||
|
||||
if self.health == 0 then
|
||||
self.health = math.random (self.hp_min, self.hp_max)
|
||||
end
|
||||
if self.breath == nil then
|
||||
self.breath = self.breath_max
|
||||
end
|
||||
if self.health == 0 then self.health = math.random(self.hp_min, self.hp_max) end
|
||||
if self.breath == nil then self.breath = self.breath_max end
|
||||
|
||||
self.path = {}
|
||||
self.path.way = {} -- path to follow, table of positions
|
||||
self.path.lastpos = {x = 0, y = 0, z = 0}
|
||||
self.path.lastpos = vector.zero()
|
||||
self.path.stuck = false
|
||||
self.path.following = false -- currently following path?
|
||||
self.path.stuck_timer = 0 -- if stuck for too long search for path
|
||||
|
@ -276,19 +216,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.blinktimer = 0
|
||||
self.blinkstatus = false
|
||||
|
||||
if not self.nametag then
|
||||
self.nametag = def.nametag
|
||||
end
|
||||
if not self.custom_visual_size then
|
||||
self.visual_size = nil
|
||||
self.base_size = self.visual_size
|
||||
if self.child then
|
||||
self.visual_size = {
|
||||
x = self.visual_size.x * 0.5,
|
||||
y = self.visual_size.y * 0.5,
|
||||
}
|
||||
end
|
||||
end
|
||||
self.nametag = self.nametag or def.nametag
|
||||
|
||||
self.object:set_properties(self)
|
||||
self:set_yaw(math.random() * math.pi * 2, 6)
|
||||
|
@ -296,22 +224,14 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self._current_animation = nil
|
||||
self:set_animation("stand")
|
||||
|
||||
|
||||
if self.riden_by_jock then --- Keep this function before self.on_spawn() is run.
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
if self.on_spawn and not self.on_spawn_run and self.on_spawn(self) then self.on_spawn_run = true end
|
||||
|
||||
if self.on_spawn and not self.on_spawn_run then
|
||||
if self.on_spawn(self) then
|
||||
self.on_spawn_run = true
|
||||
end
|
||||
end
|
||||
|
||||
if not self.wears_armor and self.armor_list then
|
||||
self.armor_list = nil
|
||||
end
|
||||
if not self.wears_armor and self.armor_list then self.armor_list = nil end
|
||||
|
||||
if not self._run_armor_init and self.wears_armor then
|
||||
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
||||
|
@ -319,15 +239,10 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self._run_armor_init = true
|
||||
end
|
||||
|
||||
if not self._mcl_potions then
|
||||
self._mcl_potions = {}
|
||||
end
|
||||
if not self._mcl_potions then self._mcl_potions = {} end
|
||||
mcl_potions._load_entity_effects(self)
|
||||
|
||||
|
||||
if def.after_activate then
|
||||
def.after_activate(self, staticdata, def, dtime)
|
||||
end
|
||||
if def.after_activate then def.after_activate(self, staticdata, def, dtime) end
|
||||
end
|
||||
|
||||
-- execute current state (stand, walk, run, attacks)
|
||||
|
@ -346,9 +261,7 @@ function mob_class:do_states(dtime, player_in_active_range)
|
|||
if self.state == PATHFINDING then
|
||||
self:check_gowp(dtime)
|
||||
elseif self.state == "attack" then
|
||||
if self:do_states_attack(dtime) then
|
||||
return true
|
||||
end
|
||||
if self:do_states_attack(dtime) then return true end
|
||||
else
|
||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_dostates", 1) then
|
||||
if self.state == "stand" then
|
||||
|
@ -364,34 +277,28 @@ end
|
|||
|
||||
function mob_class:outside_limits()
|
||||
local pos = self.object:get_pos()
|
||||
if pos then
|
||||
local posx = math.abs(pos.x)
|
||||
local posy = math.abs(pos.y)
|
||||
local posz = math.abs(pos.z)
|
||||
if posx > MAPGEN_MOB_LIMIT or posy > MAPGEN_MOB_LIMIT or posz > MAPGEN_MOB_LIMIT then
|
||||
--minetest.log("action", "Getting close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then
|
||||
minetest.log("action", "Warning mob past limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
else
|
||||
if self.state ~= "stand" then
|
||||
minetest.log("action", "Warning mob close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
self.state = "stand"
|
||||
self:set_animation("stand")
|
||||
self.object:set_acceleration(vector.zero())
|
||||
self.object:set_velocity(vector.zero())
|
||||
end
|
||||
if not pos then return end
|
||||
local posx, posy, posz = math.abs(pos.x), math.abs(pos.y), math.abs(pos.z)
|
||||
if posx > MAPGEN_MOB_LIMIT or posy > MAPGEN_MOB_LIMIT or posz > MAPGEN_MOB_LIMIT then
|
||||
--minetest.log("action", "Getting close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then
|
||||
minetest.log("action", "Warning mob past limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
else
|
||||
if self.state ~= "stand" then
|
||||
minetest.log("action", "Warning mob close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
self.state = "stand"
|
||||
self:set_animation("stand")
|
||||
self.object:set_acceleration(vector.zero())
|
||||
self.object:set_velocity(vector.zero())
|
||||
end
|
||||
return true
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function on_step_work(self, dtime, moveresult)
|
||||
local pos = self.object:get_pos()
|
||||
if not pos then return end
|
||||
|
||||
if self:check_despawn(pos, dtime) then return true end
|
||||
if self:outside_limits() then return end
|
||||
|
||||
|
@ -403,25 +310,20 @@ local function on_step_work(self, dtime, moveresult)
|
|||
end
|
||||
|
||||
if self:falling(pos, moveresult) then return end
|
||||
if self:step_damage (dtime, pos) then return end
|
||||
if self:step_damage(dtime, pos) then return end
|
||||
|
||||
if self.state == "die" then return end
|
||||
-- End: Death/damage processing
|
||||
|
||||
local player_in_active_range = self:player_in_active_range()
|
||||
self:check_suspend(player_in_active_range)
|
||||
|
||||
self:check_water_flow()
|
||||
|
||||
self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff()
|
||||
|
||||
self:flop()
|
||||
|
||||
self:check_smooth_rotation(dtime)
|
||||
|
||||
if player_in_active_range then
|
||||
self:set_animation_speed() -- set animation speed relative to velocity
|
||||
|
||||
self:check_head_swivel(dtime)
|
||||
|
||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_engage", 0.2) then
|
||||
|
@ -438,89 +340,68 @@ local function on_step_work(self, dtime, moveresult)
|
|||
end
|
||||
|
||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_occassional", 1) then
|
||||
|
||||
if player_in_active_range then
|
||||
self:check_item_pickup()
|
||||
self:set_armor_texture()
|
||||
self:step_opinion_sound(dtime)
|
||||
end
|
||||
|
||||
self:check_breeding()
|
||||
end
|
||||
|
||||
self:check_aggro(dtime)
|
||||
|
||||
self:check_particlespawners(dtime)
|
||||
|
||||
if self.do_custom and self.do_custom(self, dtime) == false then return end
|
||||
|
||||
if self:do_states(dtime, player_in_active_range) then return end
|
||||
|
||||
if mobs_debug then self:update_tag() end
|
||||
|
||||
if not self.object:get_luaentity() then
|
||||
return false
|
||||
end
|
||||
if not self.object:get_luaentity() then return false end
|
||||
end
|
||||
|
||||
local last_crash_warn_time = 0
|
||||
|
||||
local function log_error (stack_trace, info, info2)
|
||||
local function log_error(stack_trace, info, info2)
|
||||
minetest.log("action", "--- Bug report start (please provide a few lines before this also for context) ---")
|
||||
minetest.log("action", "Error: " .. stack_trace)
|
||||
minetest.log("action", "Bug info: " .. info)
|
||||
if info2 then
|
||||
minetest.log("action", "Bug info additional: " .. info2)
|
||||
end
|
||||
if info2 then minetest.log("action", "Bug info additional: " .. info2) end
|
||||
minetest.log("action", "--- Bug report end ---")
|
||||
end
|
||||
|
||||
local function warn_user_error ()
|
||||
local current_time = os.time()
|
||||
local time_since_warning = current_time - last_crash_warn_time
|
||||
|
||||
--minetest.log("previous_crash_time: " .. current_time)
|
||||
--minetest.log("last_crash_time: " .. last_crash_warn_time)
|
||||
--minetest.log("time_since_warning: " .. time_since_warning)
|
||||
|
||||
if time_since_warning > CRASH_WARN_FREQUENCY then
|
||||
last_crash_warn_time = current_time
|
||||
minetest.log("A game crashing bug was prevented. Please provide debug.log information to VoxeLibre dev team for investigation. (Search for: --- Bug report start)")
|
||||
end
|
||||
end
|
||||
|
||||
local on_step_error_handler = function ()
|
||||
warn_user_error ()
|
||||
local on_step_error_handler = function()
|
||||
warn_user_error()
|
||||
local info = debug.getinfo(1, "SnlufL")
|
||||
log_error(tostring(debug.traceback()), dump(info))
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- main mob function
|
||||
function mob_class:on_step(dtime, moveresult)
|
||||
if DEVELOPMENT then
|
||||
return on_step_work(self, dtime, moveresult)
|
||||
end
|
||||
-- allow crash in development mode
|
||||
if DEVELOPMENT then return on_step_work(self, dtime, moveresult) end
|
||||
-- Removed as bundled Lua (5.1 doesn't support xpcall)
|
||||
--local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime)
|
||||
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
|
||||
if status then
|
||||
return retVal
|
||||
end
|
||||
warn_user_error ()
|
||||
if status then return retVal end
|
||||
warn_user_error()
|
||||
local pos = self.object:get_pos()
|
||||
if pos then
|
||||
local node = minetest.get_node(pos)
|
||||
if node and node.name == "ignore" then
|
||||
minetest.log("warning", "Pos is ignored: " .. dump(pos))
|
||||
end
|
||||
if node and node.name == "ignore" then minetest.log("warning", "Pos is ignored: " .. dump(pos)) end
|
||||
end
|
||||
log_error (dump(retVal), dump(pos), dump(self))
|
||||
log_error(dump(retVal), dump(pos), dump(self))
|
||||
end
|
||||
|
||||
local timer = 0
|
||||
|
||||
local function update_lifetimer(dtime)
|
||||
timer = timer + dtime
|
||||
if timer < 1 then return end
|
||||
|
@ -541,7 +422,6 @@ minetest.register_globalstep(function(dtime)
|
|||
update_lifetimer(dtime)
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_chatcommand("clearmobs", {
|
||||
privs = { maphack = true },
|
||||
params = "[all|monster|passive|<mob name> [<range>|nametagged|tamed]]",
|
||||
|
@ -554,11 +434,7 @@ minetest.register_chatcommand("clearmobs", {
|
|||
S("Default usage. Clearing hostile mobs. For more options please type: /help clearmobs"))
|
||||
end
|
||||
local mob, unsafe = param:match("^([%w]+)[ ]?([%w%d]*)$")
|
||||
|
||||
local all = false
|
||||
local nametagged = false
|
||||
local tamed = false
|
||||
|
||||
local all, nametagged, tamed = false, false, false
|
||||
local mob_name, mob_type, range
|
||||
|
||||
-- Param 1 resolve
|
||||
|
@ -572,12 +448,7 @@ minetest.register_chatcommand("clearmobs", {
|
|||
end
|
||||
--minetest.log ("mob: [" .. mob .. "]")
|
||||
else
|
||||
--minetest.log("No valid first param")
|
||||
if default then
|
||||
--minetest.log("Use default")
|
||||
mob_type = "monster"
|
||||
end
|
||||
--return
|
||||
if default then mob_type = "monster" end
|
||||
end
|
||||
|
||||
-- Param 2 resolve
|
||||
|
@ -594,7 +465,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||
end
|
||||
|
||||
local p = minetest.get_player_by_name(player)
|
||||
|
||||
for _,o in pairs(minetest.luaentities) do
|
||||
if o and o.is_mob then
|
||||
local mob_match = false
|
||||
|
@ -603,7 +473,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||
--minetest.log("Match - All mobs specified")
|
||||
mob_match = true
|
||||
elseif mob_type then
|
||||
|
||||
--minetest.log("Match - o.type: ".. tostring(o.type))
|
||||
--minetest.log("mob_type: ".. tostring(mob_type))
|
||||
if mob_type == "monster" and o.type == mob_type then
|
||||
|
@ -615,7 +484,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||
else
|
||||
--minetest.log("No match for type.")
|
||||
end
|
||||
|
||||
elseif mob_name and (o.name == mob_name or string.find(o.name, mob_name)) then
|
||||
--minetest.log("Match - mob_name = ".. tostring(o.name))
|
||||
mob_match = true
|
||||
|
@ -626,35 +494,16 @@ minetest.register_chatcommand("clearmobs", {
|
|||
end
|
||||
|
||||
if mob_match then
|
||||
local in_range = true
|
||||
if (not range or range <= 0 ) then
|
||||
in_range = true
|
||||
else
|
||||
if ( vector.distance(p:get_pos(),o.object:get_pos()) <= range ) then
|
||||
in_range = true
|
||||
else
|
||||
--minetest.log("Out of range")
|
||||
in_range = false
|
||||
end
|
||||
end
|
||||
|
||||
--minetest.log("o.nametag: ".. tostring(o.nametag))
|
||||
|
||||
local in_range = (not range or range <= 0) or vector.distance(p:get_pos(), o.object:get_pos()) <= range
|
||||
if nametagged then
|
||||
if o.nametag then
|
||||
--minetest.log("Namedtagged and it has a name tag. Kill it")
|
||||
o.object:remove()
|
||||
end
|
||||
if o.nametag then o.object:remove() end
|
||||
elseif tamed then
|
||||
if o.tamed then
|
||||
--minetest.log("Tamed. Kill it")
|
||||
o.object:remove()
|
||||
end
|
||||
if o.tamed then o.object:remove() end
|
||||
elseif in_range and (not o.nametag or o.nametag == "") and not o.tamed then
|
||||
--minetest.log("No nametag or tamed. Kill it")
|
||||
o.object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end})
|
||||
end
|
||||
})
|
||||
|
|
|
@ -12,30 +12,29 @@ local enable_pathfinding = true
|
|||
|
||||
local TIME_TO_FORGET_TARGET = 15
|
||||
local PI = math.pi
|
||||
local HALFPI = PI * 0.5
|
||||
local random = math.random
|
||||
local min = math.min
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local abs = math.abs
|
||||
local cos = math.cos
|
||||
local sin = math.sin
|
||||
local atan2 = math.atan2
|
||||
local vector_offset = vector.offset
|
||||
local vector_new = vector.new
|
||||
local vector_copy = vector.copy
|
||||
local vector_distance = vector.distance
|
||||
|
||||
-- check if daytime and also if mob is docile during daylight hours
|
||||
function mob_class:day_docile()
|
||||
if self.docile_by_day == false then
|
||||
return false
|
||||
elseif self.docile_by_day == true
|
||||
and self.time_of_day > 0.2
|
||||
and self.time_of_day < 0.8 then
|
||||
return true
|
||||
end
|
||||
return self.docile_by_day == true and self.time_of_day > 0.2 and self.time_of_day < 0.8
|
||||
end
|
||||
|
||||
-- get this mob to attack the object
|
||||
function mob_class:do_attack(object)
|
||||
|
||||
if self.state == "attack" or self.state == "die" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if object:is_player() and not minetest.settings:get_bool("enable_damage") then
|
||||
return
|
||||
end
|
||||
if self.state == "attack" or self.state == "die" then return end
|
||||
if object:is_player() and not minetest.settings:get_bool("enable_damage") then return end
|
||||
|
||||
self.attack = object
|
||||
self.state = "attack"
|
||||
|
@ -47,21 +46,18 @@ function mob_class:do_attack(object)
|
|||
end
|
||||
|
||||
-- blast damage to entities nearby
|
||||
local function entity_physics(pos,radius)
|
||||
|
||||
local function entity_physics(pos, radius)
|
||||
radius = radius * 2
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(pos, radius)
|
||||
local obj_pos, dist
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
obj_pos = objs[n]:get_pos()
|
||||
|
||||
dist = vector.distance(pos, obj_pos)
|
||||
dist = vector_distance(pos, obj_pos)
|
||||
if dist < 1 then dist = 1 end
|
||||
|
||||
local damage = math.floor((4 / dist) * radius)
|
||||
local damage = floor((4 / dist) * radius)
|
||||
local ent = objs[n]:get_luaentity()
|
||||
|
||||
-- punches work on entities AND players
|
||||
|
@ -79,75 +75,57 @@ local height_switcher = false
|
|||
|
||||
-- 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)
|
||||
|
||||
local s1 = self.path.lastpos
|
||||
|
||||
local target_pos = self.attack:get_pos()
|
||||
|
||||
-- is it becoming stuck?
|
||||
if math.abs(s1.x - s.x) + math.abs(s1.z - s.z) < .5 then
|
||||
if abs(s1.x - s.x) + abs(s1.z - s.z) < .5 then
|
||||
self.path.stuck_timer = self.path.stuck_timer + dtime
|
||||
else
|
||||
self.path.stuck_timer = 0
|
||||
end
|
||||
|
||||
self.path.lastpos = {x = s.x, y = s.y, z = s.z}
|
||||
self.path.lastpos = vector_copy(s)
|
||||
|
||||
local use_pathfind = false
|
||||
local has_lineofsight = minetest.line_of_sight(
|
||||
{x = s.x, y = (s.y) + .5, z = s.z},
|
||||
{x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z}, .2)
|
||||
local has_lineofsight = minetest.line_of_sight(vector_offset(s, 0, .5, 0), vector_offset(target_pos, 0, 1.5, 0), .2)
|
||||
|
||||
-- im stuck, search for path
|
||||
if not has_lineofsight then
|
||||
|
||||
if los_switcher == true then
|
||||
use_pathfind = true
|
||||
los_switcher = false
|
||||
end -- cannot see target!
|
||||
else
|
||||
if los_switcher == false then
|
||||
|
||||
los_switcher = true
|
||||
use_pathfind = false
|
||||
|
||||
minetest.after(1, function(self)
|
||||
if not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not self.object:get_luaentity() then return end
|
||||
if has_lineofsight then self.path.following = false end
|
||||
end, self)
|
||||
end -- can see target!
|
||||
end
|
||||
|
||||
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
||||
|
||||
use_pathfind = true
|
||||
self.path.stuck_timer = 0
|
||||
|
||||
minetest.after(1, function(self)
|
||||
if not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not self.object:get_luaentity() then return end
|
||||
if has_lineofsight then self.path.following = false end
|
||||
end, self)
|
||||
end
|
||||
|
||||
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
||||
|
||||
use_pathfind = true
|
||||
self.path.stuck_timer = 0
|
||||
|
||||
minetest.after(1, function(self)
|
||||
if not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not self.object:get_luaentity() then return end
|
||||
if has_lineofsight then self.path.following = false end
|
||||
end, self)
|
||||
end
|
||||
|
||||
if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then
|
||||
|
||||
if abs(s.y - target_pos.y) > self.stepheight then
|
||||
if height_switcher then
|
||||
use_pathfind = true
|
||||
height_switcher = false
|
||||
|
@ -166,28 +144,21 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
|
||||
-- round position to center of node to avoid stuck in walls
|
||||
-- also adjust height for player models!
|
||||
s.x = math.floor(s.x + 0.5)
|
||||
s.z = math.floor(s.z + 0.5)
|
||||
s.x, s.z = floor(s.x + 0.5), floor(s.z + 0.5)
|
||||
|
||||
local ssight, sground = minetest.line_of_sight(s, {
|
||||
x = s.x, y = s.y - 4, z = s.z}, 1)
|
||||
local ssight, sground = minetest.line_of_sight(s, vector_offset(s, 0, -4, 0), 1)
|
||||
|
||||
-- determine node above ground
|
||||
if not ssight then
|
||||
s.y = sground.y + 1
|
||||
end
|
||||
if not ssight then s.y = sground.y + 1 end
|
||||
|
||||
local p1 = self.attack:get_pos()
|
||||
|
||||
p1.x = math.floor(p1.x + 0.5)
|
||||
p1.y = math.floor(p1.y + 0.5)
|
||||
p1.z = math.floor(p1.z + 0.5)
|
||||
p1 = vector_new(floor(p1.x + 0.5), floor(p1.y + 0.5), floor(p1.z + 0.5))
|
||||
|
||||
local dropheight = 12
|
||||
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
||||
local jumpheight = 0
|
||||
if self.jump and self.jump_height >= 4 then
|
||||
jumpheight = math.min(math.ceil(self.jump_height / 4), 4)
|
||||
jumpheight = min(ceil(self.jump_height * 0.25), 4)
|
||||
elseif self.stepheight > 0.5 then
|
||||
jumpheight = 1
|
||||
end
|
||||
|
@ -198,34 +169,27 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
|
||||
-- no path found, try something else
|
||||
if not self.path.way then
|
||||
|
||||
self.path.following = false
|
||||
|
||||
-- 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
|
||||
|
||||
-- build upwards
|
||||
if not minetest.is_protected(s, "") then
|
||||
|
||||
local ndef1 = minetest.registered_nodes[self.standing_in]
|
||||
|
||||
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
|
||||
|
||||
minetest.set_node(s, {name = mcl_mobs.fallback_node})
|
||||
end
|
||||
end
|
||||
|
||||
local sheight = math.ceil(self.collisionbox[5]) + 1
|
||||
local sheight = ceil(self.collisionbox[5]) + 1
|
||||
|
||||
-- assume mob is 2 blocks high so it digs above its head
|
||||
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]
|
||||
|
||||
|
@ -235,32 +199,21 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
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
|
||||
self.object:set_pos({x = s.x, y = s.y + 2, z = s.z})
|
||||
|
||||
self.object:set_pos(vector_offset(s, 0, 2, 0))
|
||||
else -- dig 2 blocks to make door toward player direction
|
||||
|
||||
local yaw1 = self.object:get_yaw() + math.pi / 2
|
||||
local p1 = {
|
||||
x = s.x + math.cos(yaw1),
|
||||
y = s.y,
|
||||
z = s.z + math.sin(yaw1)
|
||||
}
|
||||
local yaw1 = self.object:get_yaw() + HALFPI
|
||||
local p1 = vector_offset(s, cos(yaw1), 0, 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"
|
||||
if node1 ~= "air" and node1 ~= "ignore"
|
||||
and ndef1
|
||||
and not ndef1.groups.level
|
||||
and not ndef1.groups.unbreakable
|
||||
|
@ -274,8 +227,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
node1 = node_ok(p1, "air").name
|
||||
ndef1 = minetest.registered_nodes[node1]
|
||||
|
||||
if node1 ~= "air"
|
||||
and node1 ~= "ignore"
|
||||
if node1 ~= "air" and node1 ~= "ignore"
|
||||
and ndef1
|
||||
and not ndef1.groups.level
|
||||
and not ndef1.groups.unbreakable
|
||||
|
@ -309,28 +261,19 @@ end
|
|||
|
||||
-- specific attacks
|
||||
local specific_attack = function(list, what)
|
||||
|
||||
-- no list so attack default (player, animals etc.)
|
||||
if list == nil then
|
||||
return true
|
||||
end
|
||||
if list == nil then return true end
|
||||
|
||||
-- found entity on list to attack?
|
||||
for no = 1, #list do
|
||||
|
||||
if list[no] == what then
|
||||
return true
|
||||
end
|
||||
if list[no] == what then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- find someone to attack
|
||||
function mob_class:monster_attack()
|
||||
if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then
|
||||
return
|
||||
end
|
||||
if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then return end
|
||||
|
||||
local s = self.object:get_pos()
|
||||
local p, sp, dist
|
||||
|
@ -384,7 +327,7 @@ function mob_class:monster_attack()
|
|||
p = player:get_pos()
|
||||
sp = s
|
||||
|
||||
dist = vector.distance(p, s)
|
||||
dist = vector_distance(p, s)
|
||||
|
||||
-- aim higher to make looking up hills more realistic
|
||||
p.y = p.y + 1
|
||||
|
@ -406,7 +349,7 @@ function mob_class:monster_attack()
|
|||
end
|
||||
end
|
||||
if not min_player and #blacklist_attack > 0 then
|
||||
min_player=blacklist_attack[math.random(#blacklist_attack)]
|
||||
min_player=blacklist_attack[random(#blacklist_attack)]
|
||||
end
|
||||
-- attack player
|
||||
if min_player then
|
||||
|
@ -417,7 +360,6 @@ end
|
|||
|
||||
-- npc, find closest monster to attack
|
||||
function mob_class:npc_attack()
|
||||
|
||||
if self.type ~= "npc"
|
||||
or not self.attacks_monsters
|
||||
or self.state == "attack" then
|
||||
|
@ -436,7 +378,7 @@ function mob_class:npc_attack()
|
|||
p = obj.object:get_pos()
|
||||
sp = s
|
||||
|
||||
local dist = vector.distance(p, s)
|
||||
local dist = vector_distance(p, s)
|
||||
|
||||
-- aim higher to make looking up hills more realistic
|
||||
p.y = p.y + 1
|
||||
|
@ -458,7 +400,6 @@ end
|
|||
|
||||
-- dogshoot attack switch and counter function
|
||||
function mob_class:dogswitch(dtime)
|
||||
|
||||
-- switch mode not activated
|
||||
if not self.dogshoot_switch
|
||||
or not dtime then
|
||||
|
@ -467,10 +408,8 @@ function mob_class:dogswitch(dtime)
|
|||
|
||||
self.dogshoot_count = self.dogshoot_count + dtime
|
||||
|
||||
if (self.dogshoot_switch == 1
|
||||
and self.dogshoot_count > self.dogshoot_count_max)
|
||||
or (self.dogshoot_switch == 2
|
||||
and self.dogshoot_count > self.dogshoot_count2_max) then
|
||||
if (self.dogshoot_switch == 1 and self.dogshoot_count > self.dogshoot_count_max)
|
||||
or (self.dogshoot_switch == 2 and self.dogshoot_count > self.dogshoot_count2_max) then
|
||||
|
||||
self.dogshoot_count = 0
|
||||
|
||||
|
@ -517,13 +456,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
if is_player then
|
||||
-- is mob out of reach?
|
||||
if vector.distance(mob_pos, player_pos) > 3 then
|
||||
return
|
||||
end
|
||||
if vector_distance(mob_pos, player_pos) > 3 then return end
|
||||
-- is mob protected?
|
||||
if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then
|
||||
return
|
||||
end
|
||||
if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then return end
|
||||
|
||||
mcl_potions.update_haste_and_fatigue(hitter)
|
||||
end
|
||||
|
@ -532,13 +467,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local time_diff = time_now - self.invul_timestamp
|
||||
|
||||
-- check for invulnerability time in microseconds (0.5 second)
|
||||
if time_diff <= 500000 and time_diff >= 0 then
|
||||
return
|
||||
end
|
||||
if time_diff <= 500000 and time_diff >= 0 then return end
|
||||
|
||||
-- custom punch function
|
||||
if self.do_punch then
|
||||
|
||||
-- when false skip going any further
|
||||
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
||||
return
|
||||
|
@ -554,15 +486,11 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local time_now = minetest.get_us_time()
|
||||
|
||||
if is_player then
|
||||
if minetest.is_creative_enabled(hitter:get_player_name()) then
|
||||
self.health = 0
|
||||
end
|
||||
|
||||
if minetest.is_creative_enabled(hitter:get_player_name()) then self.health = 0 end
|
||||
-- set/update 'drop xp' timestamp if hitted by player
|
||||
self.xp_timestamp = time_now
|
||||
end
|
||||
|
||||
|
||||
-- punch interval
|
||||
local weapon = hitter:get_wielded_item()
|
||||
local punch_interval = 1.4
|
||||
|
@ -583,18 +511,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
end
|
||||
|
||||
|
||||
for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do
|
||||
|
||||
for group,_ in pairs((tool_capabilities.damage_groups or {}) ) do
|
||||
tmp = tflp / (tool_capabilities.full_punch_interval or 1.4)
|
||||
|
||||
if tmp < 0 then
|
||||
tmp = 0.0
|
||||
elseif tmp > 1 then
|
||||
tmp = 1.0
|
||||
end
|
||||
|
||||
damage = damage + (tool_capabilities.damage_groups[group] or 0)
|
||||
* tmp * ((armor[group] or 0) / 100.0)
|
||||
tmp = tmp < 0 and 0 or (tmp > 1 and 1 or tmp)
|
||||
damage = damage + (tool_capabilities.damage_groups[group] or 0) * tmp * ((armor[group] or 0) / 100.0)
|
||||
end
|
||||
|
||||
-- strength and weakness effects
|
||||
|
@ -613,9 +533,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
-- check for tool immunity or special damage
|
||||
for n = 1, #self.immune_to do
|
||||
|
||||
if self.immune_to[n][1] == weapon:get_name() then
|
||||
|
||||
damage = self.immune_to[n][2] or 0
|
||||
break
|
||||
end
|
||||
|
@ -623,7 +541,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
-- healing
|
||||
if damage <= -1 then
|
||||
self.health = self.health - math.floor(damage)
|
||||
self.health = self.health - floor(damage)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -643,7 +561,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local weapon = hitter:get_wielded_item(player)
|
||||
local def = weapon:get_definition()
|
||||
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
||||
local wear = math.floor(65535/tool_capabilities.punch_attack_uses)
|
||||
local wear = floor(65535/tool_capabilities.punch_attack_uses)
|
||||
weapon:add_wear(wear)
|
||||
tt.reload_itemstack_description(weapon) -- update tooltip
|
||||
hitter:set_wielded_item(weapon)
|
||||
|
@ -654,14 +572,12 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
local die = false
|
||||
|
||||
|
||||
if damage >= 0 then
|
||||
-- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately.
|
||||
if damage >= 0.1 then
|
||||
-- weapon sounds
|
||||
if weapon:get_definition().sounds ~= nil then
|
||||
|
||||
local s = math.random(0, #weapon:get_definition().sounds)
|
||||
local s = random(0, #weapon:get_definition().sounds)
|
||||
|
||||
minetest.sound_play(weapon:get_definition().sounds[s], {
|
||||
object = self.object, --hitter,
|
||||
|
@ -691,24 +607,18 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
if self.knock_back
|
||||
and tflp >= punch_interval then
|
||||
-- direction error check
|
||||
dir = dir or {x = 0, y = 0, z = 0}
|
||||
dir = dir or vector_zero()
|
||||
|
||||
local v = self.object:get_velocity()
|
||||
if not v then return end
|
||||
local r = 1.4 - math.min(punch_interval, 1.4)
|
||||
local kb = r * (math.abs(v.x)+math.abs(v.z))
|
||||
local r = 1.4 - min(punch_interval, 1.4)
|
||||
local kb = r * (abs(v.x)+abs(v.z))
|
||||
local up = 2.625
|
||||
|
||||
if die==true then
|
||||
kb=kb*1.25
|
||||
end
|
||||
if die then kb = kb * 1.25 end
|
||||
|
||||
-- if already in air then dont go up anymore when hit
|
||||
if math.abs(v.y) > 0.1
|
||||
or self.fly then
|
||||
up = 0
|
||||
end
|
||||
|
||||
if abs(v.y) > 0.1 or self.fly then up = 0 end
|
||||
|
||||
-- check if tool already has specific knockback value
|
||||
if tool_capabilities.damage_groups["knockback"] then
|
||||
|
@ -717,21 +627,17 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
kb = kb * 1.25
|
||||
end
|
||||
|
||||
|
||||
local luaentity
|
||||
if hitter then
|
||||
luaentity = hitter:get_luaentity()
|
||||
end
|
||||
local luaentity = hitter and hitter:get_luaentity()
|
||||
if hitter and is_player then
|
||||
local wielditem = hitter:get_wielded_item()
|
||||
kb = kb + 9 * mcl_enchanting.get_enchantment(wielditem, "knockback")
|
||||
-- add player velocity to mob knockback
|
||||
local hv = hitter:get_velocity()
|
||||
local dir_dot = (hv.x * dir.x) + (hv.z * dir.z)
|
||||
local player_mag = math.sqrt((hv.x * hv.x) + (hv.z * hv.z))
|
||||
local mob_mag = math.sqrt((v.x * v.x) + (v.z * v.z))
|
||||
local player_mag = ((hv.x * hv.x) + (hv.z * hv.z))^0.5
|
||||
local mob_mag = ((v.x * v.x) + (v.z * v.z))^0.5
|
||||
if dir_dot > 0 and mob_mag <= player_mag * 0.625 then
|
||||
kb = kb + ((math.abs(hv.x) + math.abs(hv.z)) * r)
|
||||
kb = kb + (abs(hv.x) + abs(hv.z)) * r
|
||||
end
|
||||
elseif luaentity and luaentity._knockback and die == false then
|
||||
kb = kb + luaentity._knockback
|
||||
|
@ -742,9 +648,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
self._turn_to=self.object:get_yaw()-1.57
|
||||
self.frame_speed_multiplier=2.3
|
||||
if self.animation.run_end then
|
||||
self:set_animation( "run")
|
||||
self:set_animation("run")
|
||||
elseif self.animation.walk_end then
|
||||
self:set_animation( "walk")
|
||||
self:set_animation("walk")
|
||||
end
|
||||
minetest.after(0.2, function()
|
||||
if self and self.object then
|
||||
|
@ -752,11 +658,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
self._kb_turn = false
|
||||
end
|
||||
end)
|
||||
self.object:add_velocity({
|
||||
x = dir.x * kb,
|
||||
y = up*2,
|
||||
z = dir.z * kb
|
||||
})
|
||||
self.object:add_velocity(vector_new(dir.x * kb, up*2, dir.z * kb ))
|
||||
|
||||
self.pause_timer = 0.25
|
||||
end
|
||||
|
@ -803,7 +705,6 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local obj = nil
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
obj = objs[n]:get_luaentity()
|
||||
|
||||
if obj then
|
||||
|
@ -835,11 +736,7 @@ end
|
|||
|
||||
function mob_class:check_aggro(dtime)
|
||||
if not self._aggro or not self.attack then return end
|
||||
|
||||
if not self._check_aggro_timer then
|
||||
self._check_aggro_timer = 0
|
||||
end
|
||||
|
||||
if not self._check_aggro_timer then self._check_aggro_timer = 0 end
|
||||
if self._check_aggro_timer > 5 then
|
||||
self._check_aggro_timer = 0
|
||||
|
||||
|
@ -847,7 +744,7 @@ function mob_class:check_aggro(dtime)
|
|||
-- TODO consider removing this in favour of what is done in do_states_attack
|
||||
-- Attack is dropped in do_states_attack if out of range, so won't even trigger here
|
||||
-- I do not think this code does anything. Are mobs still loaded in at 128?
|
||||
if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
||||
if not self.attack:get_pos() or vector_distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
||||
self._aggro = nil
|
||||
self.attack = nil
|
||||
self.state = "stand"
|
||||
|
@ -875,15 +772,12 @@ end
|
|||
|
||||
function mob_class:do_states_attack (dtime)
|
||||
self.timer = self.timer + dtime
|
||||
if self.timer > 100 then
|
||||
self.timer = 1
|
||||
end
|
||||
if self.timer > 100 then self.timer = 1 end
|
||||
|
||||
local s = self.object:get_pos()
|
||||
if not s then return end
|
||||
|
||||
local p = self.attack:get_pos() or s
|
||||
|
||||
local yaw = self.object:get_yaw() or 0
|
||||
|
||||
-- stop attacking if player invisible or out of range
|
||||
|
@ -915,17 +809,15 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
|
||||
-- calculate distance from mob and enemy
|
||||
local dist = vector.distance(p, s)
|
||||
local dist = vector_distance(p, s)
|
||||
|
||||
if self.attack_type == "explode" then
|
||||
|
||||
if target_line_of_sight then
|
||||
self:turn_in_direction(p.x - s.x, p.z - s.z, 1, dtime)
|
||||
end
|
||||
|
||||
local node_break_radius = self.explosion_radius or 1
|
||||
local entity_damage_radius = self.explosion_damage_radius
|
||||
or (node_break_radius * 2)
|
||||
local entity_damage_radius = self.explosion_damage_radius or (node_break_radius * 2)
|
||||
|
||||
-- start timer when in reach and line of sight
|
||||
if not self.v_start and dist <= self.reach and target_line_of_sight then
|
||||
|
@ -952,9 +844,9 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
|
||||
if self.animation and self.animation.run_start then
|
||||
self:set_animation( "run")
|
||||
self:set_animation("run")
|
||||
else
|
||||
self:set_animation( "walk")
|
||||
self:set_animation("walk")
|
||||
end
|
||||
|
||||
if self.v_start then
|
||||
|
@ -997,80 +889,47 @@ function mob_class:do_states_attack (dtime)
|
|||
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy)
|
||||
or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then
|
||||
|
||||
if self.fly
|
||||
and dist > self.reach then
|
||||
|
||||
local p1 = s
|
||||
local me_y = math.floor(p1.y)
|
||||
local p2 = p
|
||||
local p_y = math.floor(p2.y + 1)
|
||||
if self.fly and dist > self.reach then
|
||||
local p1, p2 = s, p
|
||||
local me_y, p_y = floor(p1.y), floor(p2.y + 1)
|
||||
local v = self.object:get_velocity()
|
||||
|
||||
if self:flight_check( s) then
|
||||
|
||||
if me_y < p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = 1 * self.walk_velocity,
|
||||
z = v.z
|
||||
})
|
||||
|
||||
self.object:set_velocity(vector_new(v.x, 1 * self.walk_velocity, v.z))
|
||||
elseif me_y > p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = -1 * self.walk_velocity,
|
||||
z = v.z
|
||||
})
|
||||
self.object:set_velocity(vector_new(v.x, -1 * self.walk_velocity, v.z))
|
||||
end
|
||||
else
|
||||
if me_y < p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = 0.01,
|
||||
z = v.z
|
||||
})
|
||||
|
||||
self.object:set_velocity(vector_new(v.x, 0.01, v.z))
|
||||
elseif me_y > p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = -0.01,
|
||||
z = v.z
|
||||
})
|
||||
self.object:set_velocity(vector_new(v.x, -0.01, v.z))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- rnd: new movement direction
|
||||
if self.path.following
|
||||
and self.path.way
|
||||
and self.attack_type ~= "dogshoot" then
|
||||
|
||||
if self.path.following and self.path.way and self.attack_type ~= "dogshoot" then
|
||||
-- no paths longer than 50
|
||||
if #self.path.way > 50
|
||||
or dist < self.reach then
|
||||
if #self.path.way > 50 or dist < self.reach then
|
||||
self.path.following = false
|
||||
return
|
||||
end
|
||||
|
||||
local p1 = self.path.way[1]
|
||||
|
||||
if not p1 then
|
||||
self.path.following = false
|
||||
return
|
||||
end
|
||||
|
||||
if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then
|
||||
if abs(p1.x - s.x) + abs(p1.z - s.z) < 0.6 then
|
||||
-- reached waypoint, remove it from queue
|
||||
table.remove(self.path.way, 1)
|
||||
end
|
||||
|
||||
-- set new temporary target
|
||||
p = {x = p1.x, y = p1.y, z = p1.z}
|
||||
p = vector_copy(p1)
|
||||
end
|
||||
|
||||
self:turn_in_direction(p.x - s.x, p.z - s.z, 1, dtime)
|
||||
|
@ -1111,19 +970,13 @@ function mob_class:do_states_attack (dtime)
|
|||
self.timer = 0
|
||||
|
||||
if not self.custom_attack then
|
||||
if self.double_melee_attack and math.random(1, 2) == 1 then
|
||||
if self.double_melee_attack and random(1, 2) == 1 then
|
||||
self:set_animation("punch2")
|
||||
else
|
||||
self:set_animation("punch")
|
||||
end
|
||||
|
||||
local p2 = p
|
||||
local s2 = s
|
||||
|
||||
p2.y = p2.y + .5
|
||||
s2.y = s2.y + .5
|
||||
|
||||
if self:line_of_sight( p2, s2) == true then
|
||||
if self:line_of_sight(vector_offset(p, 0, .5, 0), vector_offset(s, 0, .5, 0)) == true then
|
||||
self:mob_sound("attack")
|
||||
|
||||
-- punch player (or what player is attached to)
|
||||
|
@ -1149,54 +1002,30 @@ function mob_class:do_states_attack (dtime)
|
|||
elseif self.attack_type == "shoot"
|
||||
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1)
|
||||
or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then
|
||||
|
||||
p.y = p.y - .5
|
||||
s.y = s.y + .5
|
||||
|
||||
local vec = {
|
||||
x = p.x - s.x,
|
||||
y = p.y - s.y,
|
||||
z = p.z - s.z
|
||||
}
|
||||
local dist = (vec.x^2 + vec.y^2 + vec.z^2)^0.5
|
||||
local vec = vector_new(p.x - s.x, p.y - s.y - 1, p.z - s.z)
|
||||
local dist = (vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)^0.5
|
||||
self:turn_in_direction(vec.x, vec.z, 1, dtime)
|
||||
|
||||
local stay_away_from_player = vector.zero()
|
||||
|
||||
--strafe back and fourth
|
||||
|
||||
--stay away from player so as to shoot them
|
||||
if dist < self.avoid_distance and self.shooter_avoid_enemy then
|
||||
self:set_animation( "shoot")
|
||||
stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33)
|
||||
end
|
||||
|
||||
if self.strafes then
|
||||
if not self.strafe_direction then
|
||||
self.strafe_direction = 1.57
|
||||
end
|
||||
if math.random(40) == 1 then
|
||||
self.strafe_direction = self.strafe_direction*-1
|
||||
end
|
||||
if not self.strafe_direction then self.strafe_direction = HALFPI end
|
||||
if random(40) == 1 then self.strafe_direction = self.strafe_direction * -1 end
|
||||
|
||||
local dir = vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction)
|
||||
local dir2 = vector.multiply(dir, 0.3 * self.walk_velocity)
|
||||
|
||||
if dir2 and stay_away_from_player then
|
||||
self.acc = vector.add(dir2, stay_away_from_player)
|
||||
local dir = -atan2(p.x - s.x, p.z - s.z)
|
||||
self.acc = vector_new(-sin(dir + self.strafe_direction) * 0.8, 0, cos(dir + self.strafe_direction) * 0.8)
|
||||
--stay away from player so as to shoot them
|
||||
if self.avoid_distance and dist < self.avoid_distance and self.shooter_avoid_enemy then
|
||||
local f = 0.3 * (self.avoid_distance - dist) / self.avoid_distance
|
||||
self.acc.x, self.acc.z = self.acc.x - sin(dir) * f, self.acc.z + cos(dir) * f
|
||||
end
|
||||
else
|
||||
self:set_velocity( 0)
|
||||
self:set_velocity(0)
|
||||
end
|
||||
|
||||
local p = self.object:get_pos()
|
||||
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2
|
||||
|
||||
if self.shoot_interval
|
||||
and self.timer > self.shoot_interval
|
||||
and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next()
|
||||
and math.random(1, 100) <= 60 then
|
||||
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) * 0.5
|
||||
|
||||
if self.shoot_interval and self.timer > self.shoot_interval and random(1, 100) <= 60
|
||||
and not minetest.raycast(vector_offset(p, 0, self.shoot_offset, 0), vector_offset(self.attack:get_pos(), 0, 1.5, 0), false, false):next() then
|
||||
self.timer = 0
|
||||
self:set_animation( "shoot")
|
||||
|
||||
|
@ -1205,7 +1034,6 @@ function mob_class:do_states_attack (dtime)
|
|||
|
||||
-- Shoot arrow
|
||||
if minetest.registered_entities[self.arrow] then
|
||||
|
||||
local arrow, ent
|
||||
local v = 1
|
||||
if not self.shoot_arrow then
|
||||
|
@ -1215,9 +1043,7 @@ function mob_class:do_states_attack (dtime)
|
|||
end)
|
||||
arrow = minetest.add_entity(p, self.arrow)
|
||||
ent = arrow:get_luaentity()
|
||||
if ent.velocity then
|
||||
v = ent.velocity
|
||||
end
|
||||
v = ent.velocity or v
|
||||
ent.switch = 1
|
||||
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
|
||||
|
||||
|
@ -1231,9 +1057,7 @@ function mob_class:do_states_attack (dtime)
|
|||
|
||||
-- offset makes shoot aim accurate
|
||||
vec.y = vec.y + self.shoot_offset
|
||||
vec.x = vec.x * (v / dist)
|
||||
vec.y = vec.y * (v / dist)
|
||||
vec.z = vec.z * (v / dist)
|
||||
vec.x, vec.y, vec.z = vec.x * (v / dist), vec.y * (v / dist), vec.z * (v / dist)
|
||||
if self.shoot_arrow then
|
||||
vec = vector.normalize(vec)
|
||||
self:shoot_arrow(p, vec)
|
||||
|
@ -1242,13 +1066,9 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif self.attack_type == "custom" and self.attack_state then
|
||||
self.attack_state(self, dtime)
|
||||
end
|
||||
|
||||
if self.on_attack then
|
||||
self.on_attack(self, dtime)
|
||||
end
|
||||
|
||||
if self.on_attack then self.on_attack(self, dtime) end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,19 @@ local modname = minetest.get_current_modname()
|
|||
local path = minetest.get_modpath(modname)
|
||||
local S = minetest.get_translator(modname)
|
||||
mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
||||
|
||||
-- used by the libaries below.
|
||||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
mcl_mobs.node_ok = node_ok
|
||||
|
||||
--api and helpers
|
||||
-- effects: sounds and particles mostly
|
||||
dofile(path .. "/effects.lua")
|
||||
|
@ -19,10 +32,9 @@ dofile(path .. "/items.lua")
|
|||
dofile(path .. "/pathfinding.lua")
|
||||
-- combat: attack logic
|
||||
dofile(path .. "/combat.lua")
|
||||
-- the enity functions themselves
|
||||
-- the entity functions themselves
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
|
||||
--utility functions
|
||||
dofile(path .. "/breeding.lua")
|
||||
dofile(path .. "/spawning.lua")
|
||||
|
@ -37,16 +49,6 @@ local old_spawn_icons = minetest.settings:get_bool("mcl_old_spawn_icons",false)
|
|||
local extended_pet_control = minetest.settings:get_bool("mcl_extended_pet_control",true)
|
||||
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
|
||||
|
||||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
|
||||
--#### REGISTER FUNCS
|
||||
|
||||
-- Code to execute before custom on_rightclick handling
|
||||
|
@ -114,14 +116,8 @@ function mcl_mobs.register_mob(name, def)
|
|||
mcl_mobs.spawning_mobs[name] = true
|
||||
mcl_mobs.registered_mobs[name] = def
|
||||
|
||||
local can_despawn
|
||||
if def.can_despawn ~= nil then
|
||||
can_despawn = def.can_despawn
|
||||
elseif def.spawn_class == "passive" then
|
||||
can_despawn = false
|
||||
else
|
||||
can_despawn = true
|
||||
end
|
||||
local can_despawn = def.can_despawn
|
||||
if def.can_despawn == nil then can_despawn = def.spawn_class ~= "passive" end
|
||||
|
||||
local function scale_difficulty(value, default, min, special)
|
||||
if (not value) or (value == default) or (value == special) then
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = mcl_mobs
|
||||
author = PilzAdam
|
||||
author = PilzAdam, kno10
|
||||
description = Adds a mob API for mods to add animals or monsters, etc.
|
||||
depends = mcl_particles, mcl_luck
|
||||
optional_depends = mcl_weather, mcl_explosions, mcl_hunger, mcl_worlds, invisibility, lucky_block, cmi, doc_identifier, mcl_armor, mcl_portals, mcl_experience, mcl_sculk
|
||||
|
|
|
@ -18,10 +18,17 @@ local sin = math.sin
|
|||
local cos = math.cos
|
||||
local abs = math.abs
|
||||
local floor = math.floor
|
||||
local atan2 = math.atan2
|
||||
local PI = math.pi
|
||||
local TWOPI = 2 * math.pi
|
||||
local PIHALF = 0.5 * math.pi
|
||||
local PIQUARTER = 0.25 * math.pi
|
||||
local HALFPI = 0.5 * math.pi
|
||||
local QUARTERPI = 0.25 * math.pi
|
||||
|
||||
local vector_new = vector.new
|
||||
local vector_zero = vector.zero
|
||||
local vector_copy = vector.copy
|
||||
local vector_offset = vector.offset
|
||||
local vector_distance = vector.distance
|
||||
|
||||
local registered_fallback_node = minetest.registered_nodes[mcl_mobs.fallback_node]
|
||||
|
||||
|
@ -35,32 +42,16 @@ end
|
|||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
if fallback then
|
||||
return minetest.registered_nodes[fallback]
|
||||
else
|
||||
return registered_fallback_node
|
||||
end
|
||||
if node and minetest.registered_nodes[node.name] then return node end
|
||||
return fallback and minetest.registered_nodes[fallback] or registered_fallback_node
|
||||
end
|
||||
|
||||
-- Returns true is node can deal damage to self
|
||||
function mob_class:is_node_dangerous(nodename)
|
||||
local nn = nodename
|
||||
if self.lava_damage > 0 then
|
||||
if minetest.get_item_group(nn, "lava") ~= 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if self.fire_damage > 0 then
|
||||
if minetest.get_item_group(nn, "fire") ~= 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then
|
||||
return true
|
||||
end
|
||||
if self.lava_damage > 0 and minetest.get_item_group(nn, "lava") ~= 0 then return true end
|
||||
if self.fire_damage > 0 and minetest.get_item_group(nn, "fire") ~= 0 then return true end
|
||||
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -68,29 +59,19 @@ end
|
|||
-- Returns true if node is a water hazard
|
||||
function mob_class:is_node_waterhazard(nodename)
|
||||
local nn = nodename
|
||||
if self.water_damage > 0 then
|
||||
if minetest.get_item_group(nn, "water") ~= 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].drowning or 0) > 0 then
|
||||
if self.breath_max ~= -1 then
|
||||
-- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case
|
||||
-- this will prevent water-breathing mobs to classify water or e.g. sand below them as dangerous
|
||||
if not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.water_damage > 0 and minetest.get_item_group(nn, "water") ~= 0 then return true end
|
||||
if minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].drowning or 0) > 0
|
||||
and self.breath_max ~= -1
|
||||
-- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case
|
||||
-- this will prevent water-breathing mobs to classify water or e.g. sand below them as dangerous
|
||||
and not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function raycast_line_of_sight (origin, target)
|
||||
local function raycast_line_of_sight(origin, target)
|
||||
local raycast = minetest.raycast(origin, target, false, true)
|
||||
|
||||
local los_blocked = false
|
||||
|
||||
for hitpoint in raycast do
|
||||
if hitpoint.type == "node" then
|
||||
--TODO type object could block vision, for example chests
|
||||
|
@ -110,13 +91,11 @@ end
|
|||
|
||||
function mob_class:target_visible(origin)
|
||||
if not origin then return end
|
||||
|
||||
if not self.attack then return end
|
||||
local target_pos = self.attack:get_pos()
|
||||
|
||||
if not target_pos then return end
|
||||
|
||||
local origin_eye_pos = vector.offset(origin, 0, self.head_eye_height, 0)
|
||||
local origin_eye_pos = vector_offset(origin, 0, self.head_eye_height, 0)
|
||||
|
||||
--minetest.log("origin: " .. dump(origin))
|
||||
--minetest.log("origin_eye_pos: " .. dump(origin_eye_pos))
|
||||
|
@ -124,11 +103,11 @@ function mob_class:target_visible(origin)
|
|||
local targ_head_height, targ_feet_height
|
||||
local cbox = self.collisionbox
|
||||
if self.attack:is_player() then
|
||||
targ_head_height = vector.offset(target_pos, 0, cbox[5], 0)
|
||||
targ_head_height = vector_offset(target_pos, 0, cbox[5], 0)
|
||||
targ_feet_height = target_pos -- Cbox would put feet under ground which interferes with ray
|
||||
else
|
||||
targ_head_height = vector.offset(target_pos, 0, cbox[5], 0)
|
||||
targ_feet_height = vector.offset(target_pos, 0, cbox[2], 0)
|
||||
targ_head_height = vector_offset(target_pos, 0, cbox[5], 0)
|
||||
targ_feet_height = vector_offset(target_pos, 0, cbox[2], 0)
|
||||
end
|
||||
|
||||
--minetest.log("start targ_head_height: " .. dump(targ_head_height))
|
||||
|
@ -148,7 +127,6 @@ end
|
|||
|
||||
-- check line of sight (BrunoMine)
|
||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||
|
||||
stepsize = stepsize or 1
|
||||
|
||||
local s, pos = minetest.line_of_sight(pos1, pos2, stepsize)
|
||||
|
@ -159,8 +137,7 @@ function mob_class:line_of_sight(pos1, pos2, stepsize)
|
|||
end
|
||||
|
||||
-- New pos1 to be analyzed
|
||||
local npos1 = {x = pos1.x, y = pos1.y, z = pos1.z}
|
||||
|
||||
local npos1 = vector_copy(pos1)
|
||||
local r, pos = minetest.line_of_sight(npos1, pos2, stepsize)
|
||||
|
||||
-- Checks the return
|
||||
|
@ -170,23 +147,19 @@ function mob_class:line_of_sight(pos1, pos2, stepsize)
|
|||
local nn = minetest.get_node(pos).name
|
||||
|
||||
-- Target Distance (td) to travel
|
||||
local td = vector.distance(pos1, pos2)
|
||||
local td = vector_distance(pos1, pos2)
|
||||
|
||||
-- Actual Distance (ad) traveled
|
||||
local ad = 0
|
||||
|
||||
-- It continues to advance in the line of sight in search of a real
|
||||
-- obstruction which counts as 'normal' nodebox.
|
||||
while minetest.registered_nodes[nn]
|
||||
and minetest.registered_nodes[nn].walkable == false do
|
||||
|
||||
while minetest.registered_nodes[nn] and minetest.registered_nodes[nn].walkable == false do
|
||||
-- Check if you can still move forward
|
||||
if td < ad + stepsize then
|
||||
return true -- Reached the target
|
||||
end
|
||||
if td < ad + stepsize then return true end -- Reached the target
|
||||
|
||||
-- Moves the analyzed pos
|
||||
local d = vector.distance(pos1, pos2)
|
||||
local d = vector_distance(pos1, pos2)
|
||||
|
||||
npos1.x = ((pos2.x - pos1.x) / d * stepsize) + pos1.x
|
||||
npos1.y = ((pos2.y - pos1.y) / d * stepsize) + pos1.y
|
||||
|
@ -224,15 +197,15 @@ function mob_class:can_jump_cliff()
|
|||
local dir_z = cos(yaw) * (cbox[4] + 0.5)
|
||||
|
||||
--is there nothing under the block in front? if so jump the gap.
|
||||
local node_low = node_ok({ x = pos.x + dir_x*0.6, y = pos.y - 0.5, z = pos.z + dir_z*0.6 }, "air")
|
||||
local node_low = node_ok(vector_offset(pos, dir_x * 0.6, -0.5, dir_z * 0.6), "air")
|
||||
-- next is solid, no need to jump
|
||||
if minetest.registered_nodes[node_low.name] and minetest.registered_nodes[node_low.name].walkable == true then
|
||||
self._jumping_cliff = false
|
||||
return false
|
||||
end
|
||||
|
||||
local node_far = node_ok({ x = pos.x + dir_x*1.6, y = pos.y - 0.5, z = pos.z + dir_z*1.6 }, "air")
|
||||
local node_far2 = node_ok({ x = pos.x + dir_x*2.5, y = pos.y - 0.5, z = pos.z + dir_z*2.5 }, "air")
|
||||
local node_far = node_ok(vector_offset(pos, dir_x * 1.6, -0.5, dir_z * 1.6), "air")
|
||||
local node_far2 = node_ok(vector_offset(pos, dir_x * 2.5, -0.5, dir_z * 2.5), "air")
|
||||
-- TODO: also check there is air above these nodes?
|
||||
|
||||
-- some place to land on
|
||||
|
@ -272,8 +245,8 @@ function mob_class:is_at_cliff_or_danger()
|
|||
local ypos = pos.y + cbox[2] + 0.1 -- just above floor
|
||||
|
||||
local free_fall, blocker = minetest.line_of_sight(
|
||||
vector.new(pos.x + dir_x, ypos, pos.z + dir_z),
|
||||
vector.new(pos.x + dir_x, floor(ypos - self.fear_height), pos.z + dir_z))
|
||||
vector_new(pos.x + dir_x, ypos, pos.z + dir_z),
|
||||
vector_new(pos.x + dir_x, floor(ypos - self.fear_height), pos.z + dir_z))
|
||||
|
||||
if free_fall then
|
||||
return "free fall"
|
||||
|
@ -328,8 +301,8 @@ function mob_class:is_at_water_danger()
|
|||
local ypos = pos.y + cbox[2] + 0.1 -- just above floor
|
||||
|
||||
local los, blocker = minetest.line_of_sight(
|
||||
vector.new(pos.x + dir_x, ypos, pos.z + dir_z),
|
||||
vector.new(pos.x + dir_x, ypos - 3, pos.z + dir_z))
|
||||
vector_new(pos.x + dir_x, ypos, pos.z + dir_z),
|
||||
vector_new(pos.x + dir_x, ypos - 3, pos.z + dir_z))
|
||||
|
||||
if not los then
|
||||
local bnode = minetest.get_node(blocker)
|
||||
|
@ -374,22 +347,12 @@ end
|
|||
|
||||
-- jump if facing a solid node (not fences or gates)
|
||||
function mob_class:do_jump()
|
||||
if not self.jump
|
||||
or self.jump_height == 0
|
||||
or self.fly
|
||||
or self.order == "stand" then
|
||||
return false
|
||||
end
|
||||
|
||||
if not self.jump or self.jump_height == 0 or self.fly or self.order == "stand" then return false end
|
||||
self.facing_fence = false
|
||||
self._jumping_cliff = false
|
||||
|
||||
-- something stopping us while moving?
|
||||
if self.state ~= "stand"
|
||||
and self:get_velocity() > 0.5
|
||||
and self.object:get_velocity().y ~= 0 then
|
||||
return false
|
||||
end
|
||||
if self.state ~= "stand" and self:get_velocity() > 0.5 and self.object:get_velocity().y ~= 0 then return false end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local v = self.object:get_velocity()
|
||||
|
@ -399,10 +362,9 @@ function mob_class:do_jump()
|
|||
-- what is mob standing on?
|
||||
pos.y = pos.y + cbox[2]
|
||||
|
||||
local nodBelow = node_ok({ x = pos.x, y = pos.y - 0.2, z = pos.z })
|
||||
if minetest.registered_nodes[nodBelow.name].walkable == false and not in_water then
|
||||
return false
|
||||
end
|
||||
local node_below = node_ok(vector_offset(pos, 0, -0.2, 0))
|
||||
local nbdef = minetest.registered_nodes[node_below.name]
|
||||
if nbdef and nbdef.walkable == false and not in_water then return false end
|
||||
|
||||
local yaw = self.object:get_yaw()
|
||||
|
||||
|
@ -411,27 +373,23 @@ function mob_class:do_jump()
|
|||
local dir_z = cos(yaw) * (cbox[4] + 0.5) + v.z * 0.25
|
||||
|
||||
-- what is in front of mob?
|
||||
local nod = node_ok(vector.new(pos.x + dir_x, pos.y + 0.5, pos.z + dir_z))
|
||||
local nod = node_ok(vector_offset(pos, dir_x, 0.5, dir_z))
|
||||
|
||||
-- this is used to detect if there's a block on top of the block in front of the mob.
|
||||
-- If there is, there is no point in jumping as we won't manage.
|
||||
local nodTop = node_ok(vector.new(pos.x + dir_x, pos.y + 1.5, pos.z + dir_z), "air")
|
||||
local node_top = node_ok(vector_offset(pos, dir_x, 1.5, dir_z), "air")
|
||||
-- TODO: also check above the mob itself?
|
||||
|
||||
-- we don't attempt to jump if there's a stack of blocks blocking, unless attacking
|
||||
if minetest.registered_nodes[nodTop.name].walkable == true and not (self.attack and self.state == "attack") then
|
||||
return false
|
||||
end
|
||||
local ntdef = minetest.registered_nodes[node_top.name]
|
||||
if ntdef and ntdef.walkable == true and not (self.attack and self.state == "attack") then return false end
|
||||
|
||||
-- thin blocks that do not need to be jumped
|
||||
if nod.name == node_snow then
|
||||
return false
|
||||
end
|
||||
if nod.name == node_snow then return false end
|
||||
|
||||
local ndef = minetest.registered_nodes[nod.name]
|
||||
if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then
|
||||
return false
|
||||
end
|
||||
if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then return false end
|
||||
|
||||
if minetest.get_item_group(nod.name, "fence") ~= 0
|
||||
or minetest.get_item_group(nod.name, "fence_gate") ~= 0
|
||||
or minetest.get_item_group(nod.name, "wall") ~= 0 then
|
||||
|
@ -441,9 +399,9 @@ function mob_class:do_jump()
|
|||
|
||||
v.y = self.jump_height + 0.3
|
||||
if in_water then
|
||||
v=vector.multiply(v, vector.new(1.2,1.5,1.2))
|
||||
v.x, v.y, v.z = v.x * 1.2, v.y * 1.5, v.z * 1.2
|
||||
elseif self._can_jump_cliff then
|
||||
v=vector.multiply(v, vector.new(2.5,1.1,2.5))
|
||||
v.x, v.y, v.z = v.x * 2.5, v.y * 1.1, v.z * 2.5
|
||||
end
|
||||
|
||||
self:set_animation("jump") -- only when defined
|
||||
|
@ -451,10 +409,8 @@ function mob_class:do_jump()
|
|||
|
||||
-- when in air move forward
|
||||
local forward = function(self, v)
|
||||
if not self.object or not self.object:get_luaentity() or self.state == "die" then
|
||||
return
|
||||
end
|
||||
self.object:set_acceleration(vector.new(v.x * 2, DEFAULT_FALL_SPEED, v.z * 2))
|
||||
if not self.object or not self.object:get_luaentity() or self.state == "die" then return end
|
||||
self.object:set_acceleration(vector_new(v.x * 2, DEFAULT_FALL_SPEED, v.z * 2))
|
||||
end
|
||||
-- trying multiple time helps mobs jump
|
||||
minetest.after(0.1, forward, self, v)
|
||||
|
@ -483,28 +439,19 @@ end
|
|||
-- should mob follow what I'm holding ?
|
||||
function mob_class:follow_holding(clicker)
|
||||
if self.nofollow then return false end
|
||||
|
||||
if mcl_mobs.invis[clicker:get_player_name()] then
|
||||
return false
|
||||
end
|
||||
if mcl_mobs.invis[clicker:get_player_name()] then return false end
|
||||
|
||||
local item = clicker:get_wielded_item()
|
||||
local t = type(self.follow)
|
||||
|
||||
-- single item
|
||||
if t == "string"
|
||||
and item:get_name() == self.follow then
|
||||
if t == "string" and item:get_name() == self.follow then
|
||||
return true
|
||||
|
||||
-- multiple items
|
||||
elseif t == "table" then
|
||||
for no = 1, #self.follow do
|
||||
if self.follow[no] == item:get_name() then
|
||||
return true
|
||||
end
|
||||
if self.follow[no] == item:get_name() then return true end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -537,7 +484,6 @@ function mob_class:replace_node(pos)
|
|||
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == what then
|
||||
|
||||
local oldnode = {name = what, param2 = node.param2}
|
||||
local newnode = {name = with, param2 = node.param2}
|
||||
local on_replace_return = false
|
||||
|
@ -547,7 +493,6 @@ function mob_class:replace_node(pos)
|
|||
|
||||
|
||||
if on_replace_return ~= false then
|
||||
|
||||
if mobs_griefing then
|
||||
minetest.after(self.replace_delay, function()
|
||||
if self and self.object and self.object:get_velocity() and self.health > 0 then
|
||||
|
@ -561,32 +506,20 @@ end
|
|||
|
||||
-- specific runaway
|
||||
local specific_runaway = function(list, what)
|
||||
if type(list) ~= "table" then
|
||||
list = {}
|
||||
end
|
||||
|
||||
-- no list so do not run
|
||||
if list == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
if list == nil then return false end
|
||||
if type(list) ~= "table" then list = {} end
|
||||
-- found entity on list to attack?
|
||||
for no = 1, #list do
|
||||
|
||||
if list[no] == what then
|
||||
return true
|
||||
end
|
||||
if list[no] == what then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
-- find someone to runaway from
|
||||
function mob_class:check_runaway_from()
|
||||
if not self.runaway_from and self.state ~= "flop" then
|
||||
return
|
||||
end
|
||||
if not self.runaway_from and self.state ~= "flop" then return end
|
||||
|
||||
local s = self.object:get_pos()
|
||||
local p, sp, dist
|
||||
|
@ -596,9 +529,7 @@ function mob_class:check_runaway_from()
|
|||
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
if objs[n]:is_player() then
|
||||
|
||||
if mcl_mobs.invis[ objs[n]:get_player_name() ]
|
||||
or self.owner == objs[n]:get_player_name()
|
||||
or (not self:object_in_range(objs[n])) then
|
||||
|
@ -610,7 +541,6 @@ function mob_class:check_runaway_from()
|
|||
end
|
||||
else
|
||||
obj = objs[n]:get_luaentity()
|
||||
|
||||
if obj then
|
||||
player = obj.object
|
||||
type = obj.type
|
||||
|
@ -621,20 +551,13 @@ function mob_class:check_runaway_from()
|
|||
-- find specific mob to runaway from
|
||||
if name ~= "" and name ~= self.name
|
||||
and specific_runaway(self.runaway_from, name) then
|
||||
|
||||
p = player:get_pos()
|
||||
sp = s
|
||||
|
||||
-- aim higher to make looking up hills more realistic
|
||||
p.y = p.y + 1
|
||||
sp.y = sp.y + 1
|
||||
|
||||
dist = vector.distance(p, s)
|
||||
|
||||
|
||||
dist = vector_distance(p, s)
|
||||
-- choose closest player/mpb to runaway from
|
||||
if dist < min_dist
|
||||
and self:line_of_sight(sp, p, 2) == true then
|
||||
and self:line_of_sight(vector_offset(sp, 0, 1, 0), vector_offset(p, 0, 1, 0), 2) == true then
|
||||
-- aim higher to make looking up hills more realistic
|
||||
min_dist = dist
|
||||
min_player = player
|
||||
end
|
||||
|
@ -670,7 +593,6 @@ function mob_class:check_follow()
|
|||
|
||||
if self.type == "npc" and self.order == "follow"
|
||||
and self.state ~= "attack" and self.order ~= "sit" and self.owner ~= "" then
|
||||
|
||||
if self.following and self.owner and self.owner ~= self.following:get_player_name() then
|
||||
self.following = nil
|
||||
end
|
||||
|
@ -686,13 +608,8 @@ function mob_class:check_follow()
|
|||
-- follow that thing
|
||||
if self.following then
|
||||
local s = self.object:get_pos()
|
||||
|
||||
local p
|
||||
if self.following:is_player() then
|
||||
p = self.following:get_pos()
|
||||
elseif self.following.object then
|
||||
p = self.following.object:get_pos()
|
||||
end
|
||||
local p = self.following:is_player() and self.following:get_pos()
|
||||
or self.following.object and self.following.object:get_pos()
|
||||
|
||||
if p then
|
||||
if (not self:object_in_range(self.following)) then
|
||||
|
@ -701,7 +618,7 @@ function mob_class:check_follow()
|
|||
self:turn_in_direction(p.x - s.x, p.z - s.z, 2.35)
|
||||
|
||||
-- anyone but standing npc's can move along
|
||||
local dist = vector.distance(p, s)
|
||||
local dist = vector_distance(p, s)
|
||||
if dist > 3 and self.order ~= "stand" then
|
||||
self:set_velocity(self.follow_velocity)
|
||||
if self.walk_chance ~= 0 then
|
||||
|
@ -724,19 +641,15 @@ function mob_class:flop()
|
|||
|
||||
if self:flight_check(s) == false then
|
||||
self.state = "flop"
|
||||
self.object:set_acceleration({x = 0, y = DEFAULT_FALL_SPEED, z = 0})
|
||||
self.object:set_acceleration(vector_new(0, DEFAULT_FALL_SPEED, 0))
|
||||
|
||||
local p = self.object:get_pos()
|
||||
local sdef = minetest.registered_nodes[node_ok(vector.add(p, vector.new(0,self.collisionbox[2]-0.2,0))).name]
|
||||
local sdef = minetest.registered_nodes[node_ok(vector_offset(p, 0, self.collisionbox[2] - 0.2, 0)).name]
|
||||
-- Flop on ground
|
||||
if sdef and sdef.walkable then
|
||||
if self.object:get_velocity().y < 0.1 then
|
||||
self:mob_sound("flop")
|
||||
self.object:set_velocity({
|
||||
x = (random() * 2 - 1) * FLOP_HOR_SPEED,
|
||||
y = FLOP_HEIGHT,
|
||||
z = (random() * 2 - 1) * FLOP_HOR_SPEED,
|
||||
})
|
||||
self.object:set_velocity(vector_new((random() * 2 - 1) * FLOP_HOR_SPEED, FLOP_HEIGHT, (random() * 2 - 1) * FLOP_HOR_SPEED))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -744,7 +657,7 @@ function mob_class:flop()
|
|||
return
|
||||
elseif self.state == "flop" then
|
||||
self.state = "stand"
|
||||
self.object:set_acceleration(vector.zero())
|
||||
self.object:set_acceleration(vector_zero())
|
||||
self:set_velocity(0)
|
||||
end
|
||||
end
|
||||
|
@ -754,7 +667,7 @@ function mob_class:go_to_pos(b)
|
|||
if not self then return end
|
||||
if not b then return end
|
||||
local s = self.object:get_pos()
|
||||
if vector.distance(b,s) < .5 then return true end
|
||||
if vector_distance(b,s) < .5 then return true end
|
||||
if b.y > s.y then self:do_jump() end
|
||||
self:turn_in_direction(b.x - s.x, b.z - s.z, 2)
|
||||
self:set_velocity(self.walk_velocity)
|
||||
|
@ -784,18 +697,11 @@ function mob_class:check_herd(dtime)
|
|||
end
|
||||
|
||||
function mob_class:teleport(target)
|
||||
if self.do_teleport then
|
||||
if self.do_teleport(self, target) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
if self.do_teleport then return self.do_teleport(self, target) end
|
||||
end
|
||||
|
||||
function mob_class:animate_walk_or_fly()
|
||||
if self:flight_check()
|
||||
and self.animation
|
||||
and self.animation.fly_start
|
||||
and self.animation.fly_end then
|
||||
if self:flight_check() and self.animation and self.animation.fly_start and self.animation.fly_end then
|
||||
self:set_animation("fly")
|
||||
else
|
||||
self:set_animation("walk")
|
||||
|
@ -855,18 +761,13 @@ function mob_class:do_states_walk()
|
|||
self:stand()
|
||||
self:turn_by(PI * (random() - 0.5), 6)
|
||||
return
|
||||
elseif logging then
|
||||
minetest.log("action", "[mcl_mobs] "..self.name.." ignores the danger "..tostring(danger))
|
||||
end
|
||||
end
|
||||
-- If mob in or on dangerous block, look for land
|
||||
if self:is_node_dangerous(self.standing_in) or self:is_node_waterhazard(self.standing_in)
|
||||
or not self.fly and (self:is_node_dangerous(self.standing_on) or self:is_node_waterhazard(self.standing_on)) then
|
||||
-- Better way to find shore - copied from upstream
|
||||
local lp = minetest.find_nodes_in_area_under_air(
|
||||
vector.new(s.x - 5, s.y - 0.5, s.z - 5),
|
||||
vector.new(s.x + 5, s.y + 1, s.z + 5),
|
||||
{"group:solid"})
|
||||
local lp = minetest.find_nodes_in_area_under_air(vector_offset(s, -5, -0.5, -5), vector_offset(s, 5, 1, 5), {"group:solid"})
|
||||
-- TODO: use node with smallest change in yaw?
|
||||
|
||||
lp = #lp > 0 and lp[random(#lp)]
|
||||
|
@ -891,13 +792,13 @@ function mob_class:do_states_walk()
|
|||
-- facing wall? then turn
|
||||
local facing_wall = false
|
||||
local cbox = self.collisionbox
|
||||
local dir_x = -sin(yaw - PIQUARTER) * (cbox[4] + 0.5)
|
||||
local dir_z = cos(yaw - PIQUARTER) * (cbox[4] + 0.5)
|
||||
local nodface = node_ok({ x = s.x + dir_x, y = s.y + cbox[5] - cbox[2], z = s.z + dir_z })
|
||||
local dir_x = -sin(yaw - QUARTERPI) * (cbox[4] + 0.5)
|
||||
local dir_z = cos(yaw - QUARTERPI) * (cbox[4] + 0.5)
|
||||
local nodface = node_ok(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z))
|
||||
if minetest.registered_nodes[nodface.name] and minetest.registered_nodes[nodface.name].walkable == true then
|
||||
dir_x = -sin(yaw + PIQUARTER) * (cbox[4] + 0.5)
|
||||
dir_z = cos(yaw + PIQUARTER) * (cbox[4] + 0.5)
|
||||
nodface = node_ok({ x = s.x + dir_x, y = s.y + cbox[5] - cbox[2], z = s.z + dir_z })
|
||||
dir_x = -sin(yaw + QUARTERPI) * (cbox[4] + 0.5)
|
||||
dir_z = cos(yaw + QUARTERPI) * (cbox[4] + 0.5)
|
||||
nodface = node_ok(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z))
|
||||
if minetest.registered_nodes[nodface.name] and minetest.registered_nodes[nodface.name].walkable == true then
|
||||
facing_wall = true
|
||||
end
|
||||
|
@ -913,7 +814,7 @@ function mob_class:do_states_walk()
|
|||
if home and random() < 0.1 then
|
||||
self:turn_in_direction(home.x - s.x, home.z - s.z, 8)
|
||||
else
|
||||
self:turn_by(PIQUARTER * (random() - 0.5), 10)
|
||||
self:turn_by(QUARTERPI * (random() - 0.5), 10)
|
||||
end
|
||||
end
|
||||
self:set_velocity(self.walk_velocity)
|
||||
|
@ -941,7 +842,7 @@ function mob_class:do_states_stand(player_in_active_range)
|
|||
if home and random() < 0.3 then
|
||||
self:turn_in_direction(home.x - s.x, home.z - s.z, 8)
|
||||
else
|
||||
self:turn_by(PIHALF * (random() - 0.5), 10)
|
||||
self:turn_by(HALFPI * (random() - 0.5), 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,8 +7,17 @@ local CRAMMING_DAMAGE = 3
|
|||
local DEATH_DELAY = 0.5
|
||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||
local PI = math.pi
|
||||
local TWOPI = 2 * PI
|
||||
local HALFPI = 0.5 * PI
|
||||
local TWOPI = 2 * PI -- aka tau, but not very common
|
||||
local random = math.random
|
||||
local min = math.min
|
||||
local max = math.max
|
||||
local floor = math.floor
|
||||
local abs = math.abs
|
||||
local atan2 = math.atan2
|
||||
local sin = math.sin
|
||||
local cos = math.cos
|
||||
local node_ok = mcl_mobs.node_ok
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
|
||||
|
@ -16,20 +25,6 @@ local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
|||
local mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48
|
||||
local show_health = false
|
||||
|
||||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
|
||||
-- check if within physical map limits (-30911 to 30927)
|
||||
local function within_limits(pos, radius)
|
||||
local wmin, wmax = -30912, 30928
|
||||
|
@ -111,24 +106,21 @@ function mob_class:item_drop(cooked, looting_level)
|
|||
|
||||
local num = 0
|
||||
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
||||
if math.random() < chance then
|
||||
num = math.random(dropdef.min or 1, dropdef.max or 1)
|
||||
if random() < chance then
|
||||
num = random(dropdef.min or 1, dropdef.max or 1)
|
||||
elseif not dropdef.looting_ignore_chance then
|
||||
do_common_looting = false
|
||||
end
|
||||
|
||||
if do_common_looting then
|
||||
num = num + math.floor(math.random(0, looting_level) + 0.5)
|
||||
num = num + floor(random(0, looting_level) + 0.5)
|
||||
end
|
||||
|
||||
if num > 0 then
|
||||
item = dropdef.name
|
||||
|
||||
if cooked then
|
||||
|
||||
local output = minetest.get_craft_result({
|
||||
method = "cooking", width = 1, items = {item}})
|
||||
|
||||
local output = minetest.get_craft_result({method = "cooking", width = 1, items = {item}})
|
||||
if output and output.item and not output.item:is_empty() then
|
||||
item = output.item:get_name()
|
||||
end
|
||||
|
@ -136,12 +128,12 @@ function mob_class:item_drop(cooked, looting_level)
|
|||
|
||||
for x = 1, num do
|
||||
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
||||
end
|
||||
|
||||
if obj and obj:get_luaentity() then
|
||||
obj:set_velocity(vector.new((math.random() - 0.5) * 1.5, 6, (math.random() - 0.5) * 1.5))
|
||||
elseif obj then
|
||||
obj:remove() -- item does not exist
|
||||
if obj and obj:get_luaentity() then
|
||||
obj:set_velocity(vector.new((random() - 0.5) * 1.5, 6, (random() - 0.5) * 1.5))
|
||||
elseif obj then
|
||||
obj:remove() -- item does not exist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -199,22 +191,18 @@ function mob_class:set_velocity(v)
|
|||
end
|
||||
if v > 0 then
|
||||
local yaw = (self.object:get_yaw() or 0) + self.rotate
|
||||
local x = ((-math.sin(yaw) * v) + c_x) * .4
|
||||
local z = (( math.cos(yaw) * v) + c_z) * .4
|
||||
local x = ((-sin(yaw) * v) + c_x) * .4
|
||||
local z = (( cos(yaw) * v) + c_z) * .4
|
||||
if not self.acc then
|
||||
self.acc = vector.new(x, 0, z)
|
||||
else
|
||||
self.acc.x = x
|
||||
self.acc.y = 0
|
||||
self.acc.z = z
|
||||
self.acc.x, self.acc.y, self.acc.z = x, 0, z
|
||||
end
|
||||
else -- allow standing mobs to be pushed
|
||||
if not self.acc then
|
||||
self.acc = vector.new(c_x * .2, 0, c_z * .2)
|
||||
else
|
||||
self.acc.x = c_x * .2
|
||||
self.acc.y = 0
|
||||
self.acc.z = c_z * .2
|
||||
self.acc.x, self.acc.y, self.acc.z = c_x * .2, 0, c_z * .2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -237,7 +225,7 @@ function mob_class:update_roll()
|
|||
local cbox = table.copy(self.collisionbox)
|
||||
local acbox = self.object:get_properties().collisionbox
|
||||
|
||||
if math.abs(cbox[2] - acbox[2]) > 0.1 then
|
||||
if abs(cbox[2] - acbox[2]) > 0.1 then
|
||||
was_Fleckenstein = true
|
||||
end
|
||||
|
||||
|
@ -264,7 +252,7 @@ function mob_class:turn_by(angle, delay, dtime)
|
|||
end
|
||||
-- Turn into a direction (e.g., to the player, or away)
|
||||
function mob_class:turn_in_direction(dx, dz, delay, dtime)
|
||||
if math.abs(dx) == 0 and math.abs(dz) == 0 then return self.object:get_yaw() + self.rotate end
|
||||
if abs(dx) == 0 and abs(dz) == 0 then return self.object:get_yaw() + self.rotate end
|
||||
return self:set_yaw(-atan2(dx, dz) - self.rotate, delay, dtime)
|
||||
end
|
||||
-- set and return valid yaw
|
||||
|
@ -282,6 +270,7 @@ function mob_class:check_smooth_rotation(dtime)
|
|||
self:set_yaw(self._turn_to, .1)
|
||||
self._turn_to = nil
|
||||
end
|
||||
if not self.target_yaw then return end
|
||||
|
||||
local delay = self.delay
|
||||
local initial_yaw = self.object:get_yaw() or 0
|
||||
|
@ -299,7 +288,7 @@ function mob_class:check_smooth_rotation(dtime)
|
|||
end
|
||||
--[[ needed? if self.acc then
|
||||
local change = yaw - initial_yaw
|
||||
local si, co = math.sin(change), math.cos(change)
|
||||
local si, co = sin(change), cos(change)
|
||||
self.acc.x, self.acc.y = co * self.acc.x - si * self.acc.y, si * self.acc.x + co * self.acc.y
|
||||
end ]]--
|
||||
self.object:set_yaw(yaw)
|
||||
|
@ -416,7 +405,7 @@ function mob_class:check_for_death(cause, cmi_cause)
|
|||
|
||||
if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= math.huge) then
|
||||
local pos = self.object:get_pos()
|
||||
local xp_amount = math.random(self.xp_min, self.xp_max)
|
||||
local xp_amount = random(self.xp_min, self.xp_max)
|
||||
|
||||
if not mcl_sculk.handle_death(pos, xp_amount) then
|
||||
--minetest.log("Xp not thrown")
|
||||
|
@ -489,7 +478,7 @@ function mob_class:check_for_death(cause, cmi_cause)
|
|||
elseif self.animation and self.animation.die_start and self.animation.die_end then
|
||||
local frames = self.animation.die_end - self.animation.die_start
|
||||
local speed = self.animation.die_speed or 15
|
||||
length = math.max(frames / speed, 0) + DEATH_DELAY
|
||||
length = max(frames / speed, 0) + DEATH_DELAY
|
||||
self:set_animation( "die")
|
||||
else
|
||||
length = 1 + DEATH_DELAY
|
||||
|
@ -726,7 +715,7 @@ function mob_class:do_env_damage()
|
|||
end
|
||||
|
||||
if drowning then
|
||||
self.breath = math.max(0, self.breath - 1)
|
||||
self.breath = max(0, self.breath - 1)
|
||||
mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil)
|
||||
if self.breath <= 0 then
|
||||
local dmg
|
||||
|
@ -743,7 +732,7 @@ function mob_class:do_env_damage()
|
|||
return true
|
||||
end
|
||||
else
|
||||
self.breath = math.min(self.breath_max, self.breath + 1)
|
||||
self.breath = min(self.breath_max, self.breath + 1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -809,7 +798,7 @@ end
|
|||
|
||||
function mob_class:damage_mob(reason,damage)
|
||||
if not self.health then return end
|
||||
damage = math.floor(damage)
|
||||
damage = floor(damage)
|
||||
if damage > 0 then
|
||||
self.health = self.health - damage
|
||||
|
||||
|
@ -858,49 +847,42 @@ function mob_class:falling(pos, moveresult)
|
|||
if self.fly and self.state ~= "die" then return end
|
||||
if not self.fall_speed then self.fall_speed = DEFAULT_FALL_SPEED end
|
||||
|
||||
-- Gravity
|
||||
local v = self.object:get_velocity()
|
||||
if v then
|
||||
if v.y > 0 or (v.y <= 0 and v.y > self.fall_speed) then
|
||||
-- fall downwards at set speed
|
||||
if moveresult and moveresult.touching_ground then
|
||||
-- when touching ground, retain a minimal gravity to keep the touching_ground flag
|
||||
-- but also to not get upwards acceleration with large dtime when on bouncy ground
|
||||
self.object:set_acceleration(vector.new(0, self.fall_speed * 0.01, 0))
|
||||
else
|
||||
self.object:set_acceleration(vector.new(0, self.fall_speed, 0))
|
||||
end
|
||||
else
|
||||
-- stop accelerating once max fall speed hit
|
||||
self.object:set_acceleration(vector.zero())
|
||||
end
|
||||
end
|
||||
|
||||
if mcl_portals ~= nil then
|
||||
if mcl_portals.nether_portal_cooloff(self.object) then
|
||||
return false -- mob has teleported through Nether portal - it's 99% not falling
|
||||
end
|
||||
end
|
||||
|
||||
-- floating in water (or falling)
|
||||
local v = self.object:get_velocity()
|
||||
if v then
|
||||
local new_acceleration
|
||||
if v.y > 0 and v.y < -0.5 * DEFAULT_FALL_SPEED then
|
||||
-- when moving up, always use gravity
|
||||
new_acceleration = vector.new(0, 0.5 * DEFAULT_FALL_SPEED, 0)
|
||||
elseif v.y <= 0 and v.y > 0.5 * self.fall_speed then
|
||||
-- fall downwards at set speed
|
||||
if moveresult and moveresult.touching_ground then
|
||||
-- when touching ground, retain a minimal gravity to keep the touching_ground flag
|
||||
-- but also to not get upwards acceleration with large dtime when on bouncy ground
|
||||
new_acceleration = vector.new(0, self.fall_speed * 0.01, 0)
|
||||
else
|
||||
new_acceleration = vector.new(0, self.fall_speed, 0)
|
||||
end
|
||||
else
|
||||
-- stop accelerating once max fall speed hit
|
||||
new_acceleration = vector.zero()
|
||||
end
|
||||
self.object:set_acceleration(new_acceleration)
|
||||
end
|
||||
|
||||
local acc = self.object:get_acceleration()
|
||||
|
||||
local registered_node = minetest.registered_nodes[node_ok(pos).name]
|
||||
|
||||
if registered_node.groups.lava then
|
||||
if acc and self.floats_on_lava == 1 then
|
||||
self.object:set_acceleration(vector.new(0, -self.fall_speed / math.max(1, v.y^2), 0))
|
||||
if self.floats_on_lava == 1 then
|
||||
self.object:set_acceleration(vector.new(0, -self.fall_speed / max(1, v.y^2), 0))
|
||||
end
|
||||
end
|
||||
|
||||
-- in water then float up
|
||||
if registered_node.groups.water then
|
||||
if acc and self.floats == 1 and minetest.registered_nodes[node_ok(vector.offset(pos,0,self.collisionbox[5] -0.25,0)).name].groups.water then
|
||||
self.object:set_acceleration(vector.new(0, -self.fall_speed / math.max(1, v.y^2), 0))
|
||||
if self.floats == 1 and minetest.registered_nodes[node_ok(vector.offset(pos,0,self.collisionbox[5] -0.25,0)).name].groups.water then
|
||||
self.object:set_acceleration(vector.new(0, -self.fall_speed / max(1, v.y^2), 0))
|
||||
end
|
||||
else
|
||||
-- fall damage onto solid ground
|
||||
|
@ -960,7 +942,7 @@ function mob_class:check_dying()
|
|||
if ((self.state and self.state=="die") or self:check_for_death()) and not self.animation.die_end then
|
||||
local rot = self.object:get_rotation()
|
||||
if rot then
|
||||
rot.z = ((math.pi/2-rot.z)*.2)+rot.z
|
||||
rot.z = ((HALFPI - rot.z) * .2) + rot.z
|
||||
self.object:set_rotation(rot)
|
||||
end
|
||||
return true
|
||||
|
|
Loading…
Reference in New Issue