mirror of
https://git.minetest.land/MineClone2/MineClone2.git
synced 2024-12-04 16:53:46 +01:00
Merge pull request 'Improve mob smartness (cliffs, paths) part 1' (#4479) from kno10/VoxeLibre:leap_of_death into master
Reviewed-on: https://git.minetest.land/VoxeLibre/VoxeLibre/pulls/4479 Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
This commit is contained in:
commit
8c73164d92
@ -1,29 +1,24 @@
|
|||||||
local mob_class = mcl_mobs.mob_class
|
local mob_class = mcl_mobs.mob_class
|
||||||
local mob_class_meta = {__index = mcl_mobs.mob_class}
|
local mob_class_meta = {__index = mcl_mobs.mob_class}
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
-- API for Mobs Redo: VoxeLibre Edition
|
|
||||||
|
|
||||||
local PATHFINDING = "gowp"
|
local PATHFINDING = "gowp"
|
||||||
local CRASH_WARN_FREQUENCY = 60
|
local CRASH_WARN_FREQUENCY = 60
|
||||||
local LIFETIMER_DISTANCE = 47
|
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 S = minetest.get_translator("mcl_mobs")
|
||||||
|
|
||||||
local DEVELOPMENT = minetest.settings:get_bool("mcl_development",false)
|
|
||||||
|
|
||||||
-- Invisibility mod check
|
-- Invisibility mod check
|
||||||
mcl_mobs.invis = {}
|
mcl_mobs.invis = {}
|
||||||
|
|
||||||
local remove_far = true
|
local remove_far = true
|
||||||
|
|
||||||
local mobs_debug = minetest.settings:get_bool("mobs_debug", false) -- Shows helpful debug info above each mob
|
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 spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", true)
|
||||||
|
local DEVELOPMENT = minetest.settings:get_bool("mcl_development", 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
|
|
||||||
|
|
||||||
|
|
||||||
-- Peaceful mode message so players will know there are no monsters
|
-- Peaceful mode message so players will know there are no monsters
|
||||||
if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
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
|
function mob_class:update_tag() --update nametag and/or the debug box
|
||||||
local tag
|
local tag
|
||||||
if mobs_debug then
|
if mobs_debug then
|
||||||
local name = self.name
|
local name = self.nametag ~= "" and self.nametag or self.name
|
||||||
if self.nametag and self.nametag ~= "" then
|
|
||||||
name = self.nametag
|
|
||||||
end
|
|
||||||
tag = "name = '"..tostring(name).."'\n"..
|
tag = "name = '"..tostring(name).."'\n"..
|
||||||
"state = '"..tostring(self.state).."'\n"..
|
"state = '"..tostring(self.state).."'\n"..
|
||||||
"order = '"..tostring(self.order).."'\n"..
|
"order = '"..tostring(self.order).."'\n"..
|
||||||
@ -56,9 +48,7 @@ function mob_class:update_tag() --update nametag and/or the debug box
|
|||||||
else
|
else
|
||||||
tag = self.nametag
|
tag = self.nametag
|
||||||
end
|
end
|
||||||
self.object:set_properties({
|
self.object:set_properties({ nametag = tag })
|
||||||
nametag = tag,
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:jock_to(mob, reletive_pos, rot)
|
function mob_class:jock_to(mob, reletive_pos, rot)
|
||||||
@ -74,19 +64,15 @@ function mob_class:jock_to(mob, reletive_pos, rot)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:get_staticdata()
|
function mob_class:get_staticdata()
|
||||||
|
|
||||||
for _,p in pairs(minetest.get_connected_players()) do
|
for _,p in pairs(minetest.get_connected_players()) do
|
||||||
self:remove_particlespawners(p:get_player_name())
|
self:remove_particlespawners(p:get_player_name())
|
||||||
end
|
end
|
||||||
|
|
||||||
-- remove mob when out of range unless tamed
|
-- remove mob when out of range unless tamed
|
||||||
if remove_far
|
if remove_far and self:despawn_allowed() and self.lifetimer <= 20 then
|
||||||
and self:despawn_allowed()
|
|
||||||
and self.lifetimer <= 20 then
|
|
||||||
if spawn_logging 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")
|
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
|
end
|
||||||
|
|
||||||
return "remove"-- nil
|
return "remove"-- nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -95,17 +81,9 @@ function mob_class:get_staticdata()
|
|||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
|
|
||||||
local tmp = {}
|
local tmp = {}
|
||||||
|
|
||||||
for tag, stat in pairs(self) do
|
for tag, stat in pairs(self) do
|
||||||
|
|
||||||
local t = type(stat)
|
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
|
end
|
||||||
|
|
||||||
tmp._mcl_potions = self._mcl_potions
|
tmp._mcl_potions = self._mcl_potions
|
||||||
@ -120,10 +98,7 @@ function mob_class:get_staticdata()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function valid_texture(self, def_textures)
|
local function valid_texture(self, def_textures)
|
||||||
if not self.base_texture then
|
if not self.base_texture then return false end
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.texture_selected then
|
if self.texture_selected then
|
||||||
if #def_textures < self.texture_selected then
|
if #def_textures < self.texture_selected then
|
||||||
self.texture_selected = nil
|
self.texture_selected = nil
|
||||||
@ -148,32 +123,18 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local tmp = minetest.deserialize(staticdata)
|
local tmp = minetest.deserialize(staticdata)
|
||||||
|
|
||||||
if tmp then
|
if tmp then
|
||||||
-- Patch incorrectly converted mobs
|
-- Patch incorrectly converted mobs
|
||||||
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then
|
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then mcl_mobs.strip_staticdata(tmp) end
|
||||||
mcl_mobs.strip_staticdata(tmp)
|
for _, stat in pairs(tmp) do self[_] = stat end
|
||||||
end
|
|
||||||
|
|
||||||
for _,stat in pairs(tmp) do
|
|
||||||
self[_] = stat
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--If textures in definition change, reload textures
|
--If textures in definition change, reload textures
|
||||||
if not valid_texture(self, def.textures) then
|
if not valid_texture(self, def.textures) then
|
||||||
|
|
||||||
-- compatiblity with old simple mobs textures
|
-- compatiblity with old simple mobs textures
|
||||||
if type(def.textures[1]) == "string" then
|
if type(def.textures[1]) == "string" then def.textures = {def.textures} end
|
||||||
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
|
|
||||||
|
|
||||||
|
self.texture_selected = self.texture_selected or math.random(#def.textures)
|
||||||
self.base_texture = def.textures[self.texture_selected]
|
self.base_texture = def.textures[self.texture_selected]
|
||||||
self.base_mesh = def.mesh
|
self.base_mesh = def.mesh
|
||||||
self.base_size = self.visual_size
|
self.base_size = self.visual_size
|
||||||
@ -181,9 +142,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||||||
self.base_selbox = self.selectionbox
|
self.base_selbox = self.selectionbox
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self.base_selbox then
|
self.base_selbox = self.base_selbox or self.selectionbox or self.base_colbox
|
||||||
self.base_selbox = self.selectionbox or self.base_colbox
|
|
||||||
end
|
|
||||||
|
|
||||||
local textures = self.base_texture
|
local textures = self.base_texture
|
||||||
local mesh = self.base_mesh
|
local mesh = self.base_mesh
|
||||||
@ -191,26 +150,11 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||||||
local colbox = self.base_colbox
|
local colbox = self.base_colbox
|
||||||
local selbox = self.base_selbox
|
local selbox = self.base_selbox
|
||||||
|
|
||||||
if self.gotten == true
|
if self.gotten and def.gotten_texture then textures = def.gotten_texture end
|
||||||
and def.gotten_texture then
|
if self.gotten and def.gotten_mesh then mesh = def.gotten_mesh end
|
||||||
textures = def.gotten_texture
|
if self.child then
|
||||||
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
|
||||||
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
|
|
||||||
|
|
||||||
colbox = {
|
colbox = {
|
||||||
self.base_colbox[1] * .5,
|
self.base_colbox[1] * .5,
|
||||||
@ -230,16 +174,12 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.health == 0 then
|
if self.health == 0 then self.health = math.random(self.hp_min, self.hp_max) end
|
||||||
self.health = math.random (self.hp_min, self.hp_max)
|
if self.breath == nil then self.breath = self.breath_max end
|
||||||
end
|
|
||||||
if self.breath == nil then
|
|
||||||
self.breath = self.breath_max
|
|
||||||
end
|
|
||||||
|
|
||||||
self.path = {}
|
self.path = {}
|
||||||
self.path.way = {} -- path to follow, table of positions
|
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.stuck = false
|
||||||
self.path.following = false -- currently following path?
|
self.path.following = false -- currently following path?
|
||||||
self.path.stuck_timer = 0 -- if stuck for too long search for path
|
self.path.stuck_timer = 0 -- if stuck for too long search for path
|
||||||
@ -276,42 +216,22 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||||||
self.blinktimer = 0
|
self.blinktimer = 0
|
||||||
self.blinkstatus = false
|
self.blinkstatus = false
|
||||||
|
|
||||||
if not self.nametag then
|
self.nametag = self.nametag or def.nametag
|
||||||
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.object:set_properties(self)
|
self.object:set_properties(self)
|
||||||
self:set_yaw( (math.random(0, 360) - 180) / 180 * math.pi, 6)
|
self:set_yaw(math.random() * math.pi * 2, 6)
|
||||||
self:update_tag()
|
self:update_tag()
|
||||||
self._current_animation = nil
|
self._current_animation = nil
|
||||||
self:set_animation( "stand")
|
self:set_animation("stand")
|
||||||
|
|
||||||
|
if self.riden_by_jock then --- Keep this function before self:on_spawn()
|
||||||
if self.riden_by_jock then --- Keep this function before self.on_spawn() is run.
|
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.on_spawn and not self.on_spawn_run and self:on_spawn() then self.on_spawn_run = true end
|
||||||
|
|
||||||
if self.on_spawn and not self.on_spawn_run then
|
if not self.wears_armor and self.armor_list then self.armor_list = nil end
|
||||||
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._run_armor_init and self.wears_armor then
|
if not self._run_armor_init and self.wears_armor then
|
||||||
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
||||||
@ -319,15 +239,10 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||||||
self._run_armor_init = true
|
self._run_armor_init = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self._mcl_potions then
|
if not self._mcl_potions then self._mcl_potions = {} end
|
||||||
self._mcl_potions = {}
|
|
||||||
end
|
|
||||||
mcl_potions._load_entity_effects(self)
|
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
|
end
|
||||||
|
|
||||||
-- execute current state (stand, walk, run, attacks)
|
-- 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
|
if self.state == PATHFINDING then
|
||||||
self:check_gowp(dtime)
|
self:check_gowp(dtime)
|
||||||
elseif self.state == "attack" then
|
elseif self.state == "attack" then
|
||||||
if self:do_states_attack(dtime) then
|
if self:do_states_attack(dtime) then return true end
|
||||||
return true
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_dostates", 1) then
|
if mcl_util.check_dtime_timer(self, dtime, "onstep_dostates", 1) then
|
||||||
if self.state == "stand" then
|
if self.state == "stand" then
|
||||||
@ -364,10 +277,8 @@ end
|
|||||||
|
|
||||||
function mob_class:outside_limits()
|
function mob_class:outside_limits()
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if pos then
|
if not pos then return end
|
||||||
local posx = math.abs(pos.x)
|
local posx, posy, posz = math.abs(pos.x), math.abs(pos.y), math.abs(pos.z)
|
||||||
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
|
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))
|
--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
|
if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then
|
||||||
@ -383,15 +294,11 @@ function mob_class:outside_limits()
|
|||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local function on_step_work(self, dtime, moveresult)
|
local function on_step_work(self, dtime, moveresult)
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not pos then return end
|
if not pos then return end
|
||||||
|
|
||||||
if self:check_despawn(pos, dtime) then return true end
|
if self:check_despawn(pos, dtime) then return true end
|
||||||
if self:outside_limits() then return end
|
if self:outside_limits() then return end
|
||||||
|
|
||||||
@ -403,29 +310,20 @@ local function on_step_work(self, dtime, moveresult)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self:falling(pos, moveresult) then return 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
|
if self.state == "die" then return end
|
||||||
-- End: Death/damage processing
|
-- End: Death/damage processing
|
||||||
|
|
||||||
local player_in_active_range = self:player_in_active_range()
|
local player_in_active_range = self:player_in_active_range()
|
||||||
self:check_suspend(player_in_active_range)
|
self:check_suspend(player_in_active_range)
|
||||||
|
|
||||||
self:check_water_flow()
|
self:check_water_flow()
|
||||||
|
self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff()
|
||||||
if not self._jumping_cliff then
|
|
||||||
self._can_jump_cliff = self:can_jump_cliff()
|
|
||||||
else
|
|
||||||
self._can_jump_cliff = false
|
|
||||||
end
|
|
||||||
|
|
||||||
self:flop()
|
self:flop()
|
||||||
|
|
||||||
self:check_smooth_rotation(dtime)
|
self:check_smooth_rotation(dtime)
|
||||||
|
|
||||||
if player_in_active_range then
|
if player_in_active_range then
|
||||||
self:set_animation_speed() -- set animation speed relative to velocity
|
self:set_animation_speed() -- set animation speed relative to velocity
|
||||||
|
|
||||||
self:check_head_swivel(dtime)
|
self:check_head_swivel(dtime)
|
||||||
|
|
||||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_engage", 0.2) then
|
if mcl_util.check_dtime_timer(self, dtime, "onstep_engage", 0.2) then
|
||||||
@ -442,91 +340,68 @@ local function on_step_work(self, dtime, moveresult)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_occassional", 1) then
|
if mcl_util.check_dtime_timer(self, dtime, "onstep_occassional", 1) then
|
||||||
|
|
||||||
if player_in_active_range then
|
if player_in_active_range then
|
||||||
self:check_item_pickup()
|
self:check_item_pickup()
|
||||||
self:set_armor_texture()
|
self:set_armor_texture()
|
||||||
self:step_opinion_sound(dtime)
|
self:step_opinion_sound(dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:check_breeding()
|
self:check_breeding()
|
||||||
end
|
end
|
||||||
|
|
||||||
self:check_aggro(dtime)
|
self:check_aggro(dtime)
|
||||||
|
|
||||||
self:check_particlespawners(dtime)
|
self:check_particlespawners(dtime)
|
||||||
|
|
||||||
if self.do_custom and self.do_custom(self, dtime) == false then return end
|
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 self:do_states(dtime, player_in_active_range) then return end
|
||||||
|
|
||||||
if mobs_debug then self:update_tag() 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
|
end
|
||||||
|
|
||||||
local last_crash_warn_time = 0
|
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", "--- Bug report start (please provide a few lines before this also for context) ---")
|
||||||
minetest.log("action", "Error: " .. stack_trace)
|
minetest.log("action", "Error: " .. stack_trace)
|
||||||
minetest.log("action", "Bug info: " .. info)
|
minetest.log("action", "Bug info: " .. info)
|
||||||
if info2 then
|
if info2 then minetest.log("action", "Bug info additional: " .. info2) end
|
||||||
minetest.log("action", "Bug info additional: " .. info2)
|
|
||||||
end
|
|
||||||
minetest.log("action", "--- Bug report end ---")
|
minetest.log("action", "--- Bug report end ---")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function warn_user_error ()
|
local function warn_user_error ()
|
||||||
local current_time = os.time()
|
local current_time = os.time()
|
||||||
local time_since_warning = current_time - last_crash_warn_time
|
local time_since_warning = current_time - last_crash_warn_time
|
||||||
|
|
||||||
--minetest.log("previous_crash_time: " .. current_time)
|
--minetest.log("previous_crash_time: " .. current_time)
|
||||||
--minetest.log("last_crash_time: " .. last_crash_warn_time)
|
--minetest.log("last_crash_time: " .. last_crash_warn_time)
|
||||||
--minetest.log("time_since_warning: " .. time_since_warning)
|
--minetest.log("time_since_warning: " .. time_since_warning)
|
||||||
|
|
||||||
if time_since_warning > CRASH_WARN_FREQUENCY then
|
if time_since_warning > CRASH_WARN_FREQUENCY then
|
||||||
last_crash_warn_time = current_time
|
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)")
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
local on_step_error_handler = function ()
|
local on_step_error_handler = function()
|
||||||
warn_user_error ()
|
warn_user_error()
|
||||||
local info = debug.getinfo(1, "SnlufL")
|
local info = debug.getinfo(1, "SnlufL")
|
||||||
log_error(tostring(debug.traceback()), dump(info))
|
log_error(tostring(debug.traceback()), dump(info))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- main mob function
|
-- main mob function
|
||||||
function mob_class:on_step(dtime, moveresult)
|
function mob_class:on_step(dtime, moveresult)
|
||||||
if not DEVELOPMENT then
|
-- 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)
|
-- 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 = xpcall(on_step_work, on_step_error_handler, self, dtime)
|
||||||
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
|
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
|
||||||
if status then
|
if status then return retVal end
|
||||||
return retVal
|
warn_user_error()
|
||||||
else
|
|
||||||
warn_user_error ()
|
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if pos then
|
if pos then
|
||||||
local node = minetest.get_node(pos)
|
local node = minetest.get_node(pos)
|
||||||
if node and node.name == "ignore" then
|
if node and node.name == "ignore" then minetest.log("warning", "Pos is ignored: " .. dump(pos)) end
|
||||||
minetest.log("warning", "Pos is ignored: " .. dump(pos))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
log_error (dump(retVal), dump(pos), dump(self))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return on_step_work (self, dtime, moveresult)
|
|
||||||
end
|
end
|
||||||
|
log_error(dump(retVal), dump(pos), dump(self))
|
||||||
end
|
end
|
||||||
|
|
||||||
local timer = 0
|
local timer = 0
|
||||||
|
|
||||||
local function update_lifetimer(dtime)
|
local function update_lifetimer(dtime)
|
||||||
timer = timer + dtime
|
timer = timer + dtime
|
||||||
if timer < 1 then return end
|
if timer < 1 then return end
|
||||||
@ -547,7 +422,6 @@ minetest.register_globalstep(function(dtime)
|
|||||||
update_lifetimer(dtime)
|
update_lifetimer(dtime)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
minetest.register_chatcommand("clearmobs", {
|
minetest.register_chatcommand("clearmobs", {
|
||||||
privs = { maphack = true },
|
privs = { maphack = true },
|
||||||
params = "[all|monster|passive|<mob name> [<range>|nametagged|tamed]]",
|
params = "[all|monster|passive|<mob name> [<range>|nametagged|tamed]]",
|
||||||
@ -560,11 +434,7 @@ minetest.register_chatcommand("clearmobs", {
|
|||||||
S("Default usage. Clearing hostile mobs. For more options please type: /help clearmobs"))
|
S("Default usage. Clearing hostile mobs. For more options please type: /help clearmobs"))
|
||||||
end
|
end
|
||||||
local mob, unsafe = param:match("^([%w]+)[ ]?([%w%d]*)$")
|
local mob, unsafe = param:match("^([%w]+)[ ]?([%w%d]*)$")
|
||||||
|
local all, nametagged, tamed = false, false, false
|
||||||
local all = false
|
|
||||||
local nametagged = false
|
|
||||||
local tamed = false
|
|
||||||
|
|
||||||
local mob_name, mob_type, range
|
local mob_name, mob_type, range
|
||||||
|
|
||||||
-- Param 1 resolve
|
-- Param 1 resolve
|
||||||
@ -578,12 +448,7 @@ minetest.register_chatcommand("clearmobs", {
|
|||||||
end
|
end
|
||||||
--minetest.log ("mob: [" .. mob .. "]")
|
--minetest.log ("mob: [" .. mob .. "]")
|
||||||
else
|
else
|
||||||
--minetest.log("No valid first param")
|
if default then mob_type = "monster" end
|
||||||
if default then
|
|
||||||
--minetest.log("Use default")
|
|
||||||
mob_type = "monster"
|
|
||||||
end
|
|
||||||
--return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Param 2 resolve
|
-- Param 2 resolve
|
||||||
@ -600,7 +465,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||||||
end
|
end
|
||||||
|
|
||||||
local p = minetest.get_player_by_name(player)
|
local p = minetest.get_player_by_name(player)
|
||||||
|
|
||||||
for _,o in pairs(minetest.luaentities) do
|
for _,o in pairs(minetest.luaentities) do
|
||||||
if o and o.is_mob then
|
if o and o.is_mob then
|
||||||
local mob_match = false
|
local mob_match = false
|
||||||
@ -609,7 +473,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||||||
--minetest.log("Match - All mobs specified")
|
--minetest.log("Match - All mobs specified")
|
||||||
mob_match = true
|
mob_match = true
|
||||||
elseif mob_type then
|
elseif mob_type then
|
||||||
|
|
||||||
--minetest.log("Match - o.type: ".. tostring(o.type))
|
--minetest.log("Match - o.type: ".. tostring(o.type))
|
||||||
--minetest.log("mob_type: ".. tostring(mob_type))
|
--minetest.log("mob_type: ".. tostring(mob_type))
|
||||||
if mob_type == "monster" and o.type == mob_type then
|
if mob_type == "monster" and o.type == mob_type then
|
||||||
@ -621,7 +484,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||||||
else
|
else
|
||||||
--minetest.log("No match for type.")
|
--minetest.log("No match for type.")
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif mob_name and (o.name == mob_name or string.find(o.name, mob_name)) then
|
elseif mob_name and (o.name == mob_name or string.find(o.name, mob_name)) then
|
||||||
--minetest.log("Match - mob_name = ".. tostring(o.name))
|
--minetest.log("Match - mob_name = ".. tostring(o.name))
|
||||||
mob_match = true
|
mob_match = true
|
||||||
@ -632,35 +494,16 @@ minetest.register_chatcommand("clearmobs", {
|
|||||||
end
|
end
|
||||||
|
|
||||||
if mob_match then
|
if mob_match then
|
||||||
local in_range = true
|
local in_range = (not range or range <= 0) or vector.distance(p:get_pos(), o.object:get_pos()) <= range
|
||||||
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))
|
|
||||||
|
|
||||||
if nametagged then
|
if nametagged then
|
||||||
if o.nametag then
|
if o.nametag then o.object:remove() end
|
||||||
--minetest.log("Namedtagged and it has a name tag. Kill it")
|
|
||||||
o.object:remove()
|
|
||||||
end
|
|
||||||
elseif tamed then
|
elseif tamed then
|
||||||
if o.tamed then
|
if o.tamed then o.object:remove() end
|
||||||
--minetest.log("Tamed. Kill it")
|
|
||||||
o.object:remove()
|
|
||||||
end
|
|
||||||
elseif in_range and (not o.nametag or o.nametag == "") and not o.tamed then
|
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()
|
o.object:remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end})
|
end
|
||||||
|
})
|
||||||
|
@ -63,7 +63,7 @@ function mob_class:feed_tame(clicker, feed_count, breed, tame, notake)
|
|||||||
|
|
||||||
-- make children grow quicker
|
-- make children grow quicker
|
||||||
|
|
||||||
if not consume_food and self.child == true then
|
if not consume_food and self.child then
|
||||||
consume_food = true
|
consume_food = true
|
||||||
-- deduct 10% of the time to adulthood
|
-- deduct 10% of the time to adulthood
|
||||||
self.hornytimer = self.hornytimer + ((CHILD_GROW_TIME - self.hornytimer) * 0.1)
|
self.hornytimer = self.hornytimer + ((CHILD_GROW_TIME - self.hornytimer) * 0.1)
|
||||||
@ -158,7 +158,7 @@ function mob_class:check_breeding()
|
|||||||
|
|
||||||
--mcl_log("In breed function")
|
--mcl_log("In breed function")
|
||||||
-- child takes a long time before growing into adult
|
-- child takes a long time before growing into adult
|
||||||
if self.child == true then
|
if self.child then
|
||||||
|
|
||||||
-- When a child, hornytimer is used to count age until adulthood
|
-- When a child, hornytimer is used to count age until adulthood
|
||||||
self.hornytimer = self.hornytimer + 1
|
self.hornytimer = self.hornytimer + 1
|
||||||
|
@ -11,39 +11,30 @@ local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
|||||||
local enable_pathfinding = true
|
local enable_pathfinding = true
|
||||||
|
|
||||||
local TIME_TO_FORGET_TARGET = 15
|
local TIME_TO_FORGET_TARGET = 15
|
||||||
|
local PI = math.pi
|
||||||
local atann = math.atan
|
local HALFPI = PI * 0.5
|
||||||
local function atan(x)
|
local random = math.random
|
||||||
if not x or x ~= x then
|
local min = math.min
|
||||||
return 0
|
local floor = math.floor
|
||||||
else
|
local ceil = math.ceil
|
||||||
return atann(x)
|
local abs = math.abs
|
||||||
end
|
local cos = math.cos
|
||||||
end
|
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
|
-- check if daytime and also if mob is docile during daylight hours
|
||||||
function mob_class:day_docile()
|
function mob_class:day_docile()
|
||||||
if self.docile_by_day == false then
|
return self.docile_by_day == true and self.time_of_day > 0.2 and self.time_of_day < 0.8
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get this mob to attack the object
|
-- get this mob to attack the object
|
||||||
function mob_class:do_attack(object)
|
function mob_class:do_attack(object)
|
||||||
|
if self.state == "attack" or self.state == "die" then return end
|
||||||
if self.state == "attack" or self.state == "die" then
|
if object:is_player() and not minetest.settings:get_bool("enable_damage") then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if object:is_player() and not minetest.settings:get_bool("enable_damage") then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
self.attack = object
|
self.attack = object
|
||||||
self.state = "attack"
|
self.state = "attack"
|
||||||
@ -55,21 +46,18 @@ function mob_class:do_attack(object)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- blast damage to entities nearby
|
-- blast damage to entities nearby
|
||||||
local function entity_physics(pos,radius)
|
local function entity_physics(pos, radius)
|
||||||
|
|
||||||
radius = radius * 2
|
radius = radius * 2
|
||||||
|
|
||||||
local objs = minetest.get_objects_inside_radius(pos, radius)
|
local objs = minetest.get_objects_inside_radius(pos, radius)
|
||||||
local obj_pos, dist
|
local obj_pos, dist
|
||||||
|
|
||||||
for n = 1, #objs do
|
for n = 1, #objs do
|
||||||
|
|
||||||
obj_pos = objs[n]:get_pos()
|
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
|
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()
|
local ent = objs[n]:get_luaentity()
|
||||||
|
|
||||||
-- punches work on entities AND players
|
-- punches work on entities AND players
|
||||||
@ -87,75 +75,57 @@ local height_switcher = false
|
|||||||
|
|
||||||
-- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3
|
-- 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)
|
function mob_class:smart_mobs(s, p, dist, dtime)
|
||||||
|
|
||||||
local s1 = self.path.lastpos
|
local s1 = self.path.lastpos
|
||||||
|
|
||||||
local target_pos = self.attack:get_pos()
|
local target_pos = self.attack:get_pos()
|
||||||
|
|
||||||
-- is it becoming stuck?
|
-- 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
|
self.path.stuck_timer = self.path.stuck_timer + dtime
|
||||||
else
|
else
|
||||||
self.path.stuck_timer = 0
|
self.path.stuck_timer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
self.path.lastpos = {x = s.x, y = s.y, z = s.z}
|
self.path.lastpos = vector_copy(s)
|
||||||
|
|
||||||
local use_pathfind = false
|
local use_pathfind = false
|
||||||
local has_lineofsight = minetest.line_of_sight(
|
local has_lineofsight = minetest.line_of_sight(vector_offset(s, 0, .5, 0), vector_offset(target_pos, 0, 1.5, 0), .2)
|
||||||
{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)
|
|
||||||
|
|
||||||
-- im stuck, search for path
|
-- im stuck, search for path
|
||||||
if not has_lineofsight then
|
if not has_lineofsight then
|
||||||
|
|
||||||
if los_switcher == true then
|
if los_switcher == true then
|
||||||
use_pathfind = true
|
use_pathfind = true
|
||||||
los_switcher = false
|
los_switcher = false
|
||||||
end -- cannot see target!
|
end -- cannot see target!
|
||||||
else
|
else
|
||||||
if los_switcher == false then
|
if los_switcher == false then
|
||||||
|
|
||||||
los_switcher = true
|
los_switcher = true
|
||||||
use_pathfind = false
|
use_pathfind = false
|
||||||
|
|
||||||
minetest.after(1, function(self)
|
minetest.after(1, function(self)
|
||||||
if not self.object:get_luaentity() then
|
if not self.object:get_luaentity() then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
if has_lineofsight then self.path.following = false end
|
if has_lineofsight then self.path.following = false end
|
||||||
end, self)
|
end, self)
|
||||||
end -- can see target!
|
end -- can see target!
|
||||||
end
|
end
|
||||||
|
|
||||||
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
||||||
|
|
||||||
use_pathfind = true
|
use_pathfind = true
|
||||||
self.path.stuck_timer = 0
|
self.path.stuck_timer = 0
|
||||||
|
|
||||||
minetest.after(1, function(self)
|
minetest.after(1, function(self)
|
||||||
if not self.object:get_luaentity() then
|
if not self.object:get_luaentity() then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
if has_lineofsight then self.path.following = false end
|
if has_lineofsight then self.path.following = false end
|
||||||
end, self)
|
end, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
||||||
|
|
||||||
use_pathfind = true
|
use_pathfind = true
|
||||||
self.path.stuck_timer = 0
|
self.path.stuck_timer = 0
|
||||||
|
|
||||||
minetest.after(1, function(self)
|
minetest.after(1, function(self)
|
||||||
if not self.object:get_luaentity() then
|
if not self.object:get_luaentity() then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
if has_lineofsight then self.path.following = false end
|
if has_lineofsight then self.path.following = false end
|
||||||
end, self)
|
end, self)
|
||||||
end
|
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
|
if height_switcher then
|
||||||
use_pathfind = true
|
use_pathfind = true
|
||||||
height_switcher = false
|
height_switcher = false
|
||||||
@ -174,28 +144,21 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||||||
|
|
||||||
-- round position to center of node to avoid stuck in walls
|
-- round position to center of node to avoid stuck in walls
|
||||||
-- also adjust height for player models!
|
-- also adjust height for player models!
|
||||||
s.x = math.floor(s.x + 0.5)
|
s.x, s.z = floor(s.x + 0.5), floor(s.z + 0.5)
|
||||||
s.z = math.floor(s.z + 0.5)
|
|
||||||
|
|
||||||
local ssight, sground = minetest.line_of_sight(s, {
|
local ssight, sground = minetest.line_of_sight(s, vector_offset(s, 0, -4, 0), 1)
|
||||||
x = s.x, y = s.y - 4, z = s.z}, 1)
|
|
||||||
|
|
||||||
-- determine node above ground
|
-- determine node above ground
|
||||||
if not ssight then
|
if not ssight then s.y = sground.y + 1 end
|
||||||
s.y = sground.y + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local p1 = self.attack:get_pos()
|
local p1 = self.attack:get_pos()
|
||||||
|
p1 = vector_new(floor(p1.x + 0.5), floor(p1.y + 0.5), floor(p1.z + 0.5))
|
||||||
p1.x = math.floor(p1.x + 0.5)
|
|
||||||
p1.y = math.floor(p1.y + 0.5)
|
|
||||||
p1.z = math.floor(p1.z + 0.5)
|
|
||||||
|
|
||||||
local dropheight = 12
|
local dropheight = 12
|
||||||
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
||||||
local jumpheight = 0
|
local jumpheight = 0
|
||||||
if self.jump and self.jump_height >= 4 then
|
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
|
elseif self.stepheight > 0.5 then
|
||||||
jumpheight = 1
|
jumpheight = 1
|
||||||
end
|
end
|
||||||
@ -206,34 +169,27 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||||||
|
|
||||||
-- no path found, try something else
|
-- no path found, try something else
|
||||||
if not self.path.way then
|
if not self.path.way then
|
||||||
|
|
||||||
self.path.following = false
|
self.path.following = false
|
||||||
|
|
||||||
-- 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 higher than mob?
|
||||||
if s.y < p1.y then
|
if s.y < p1.y then
|
||||||
|
|
||||||
-- build upwards
|
-- build upwards
|
||||||
if not minetest.is_protected(s, "") then
|
if not minetest.is_protected(s, "") then
|
||||||
|
|
||||||
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 = mcl_mobs.fallback_node})
|
minetest.set_node(s, {name = mcl_mobs.fallback_node})
|
||||||
end
|
end
|
||||||
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
|
-- assume mob is 2 blocks high so it digs above its head
|
||||||
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
|
if not minetest.is_protected(s, "") then
|
||||||
|
|
||||||
local node1 = node_ok(s, "air").name
|
local node1 = node_ok(s, "air").name
|
||||||
local ndef1 = minetest.registered_nodes[node1]
|
local ndef1 = minetest.registered_nodes[node1]
|
||||||
|
|
||||||
@ -243,32 +199,21 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||||||
and not ndef1.groups.level
|
and not ndef1.groups.level
|
||||||
and not ndef1.groups.unbreakable
|
and not ndef1.groups.unbreakable
|
||||||
and not ndef1.groups.liquid then
|
and not ndef1.groups.liquid then
|
||||||
|
|
||||||
minetest.set_node(s, {name = "air"})
|
minetest.set_node(s, {name = "air"})
|
||||||
minetest.add_item(s, ItemStack(node1))
|
minetest.add_item(s, ItemStack(node1))
|
||||||
|
|
||||||
end
|
end
|
||||||
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(vector_offset(s, 0, 2, 0))
|
||||||
|
|
||||||
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() + HALFPI
|
||||||
local yaw1 = self.object:get_yaw() + math.pi / 2
|
local p1 = vector_offset(s, cos(yaw1), 0, sin(yaw1))
|
||||||
local p1 = {
|
|
||||||
x = s.x + math.cos(yaw1),
|
|
||||||
y = s.y,
|
|
||||||
z = s.z + math.sin(yaw1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if not minetest.is_protected(p1, "") then
|
if not minetest.is_protected(p1, "") then
|
||||||
|
|
||||||
local node1 = node_ok(p1, "air").name
|
local node1 = node_ok(p1, "air").name
|
||||||
local ndef1 = minetest.registered_nodes[node1]
|
local ndef1 = minetest.registered_nodes[node1]
|
||||||
|
if node1 ~= "air" and node1 ~= "ignore"
|
||||||
if node1 ~= "air"
|
|
||||||
and node1 ~= "ignore"
|
|
||||||
and ndef1
|
and ndef1
|
||||||
and not ndef1.groups.level
|
and not ndef1.groups.level
|
||||||
and not ndef1.groups.unbreakable
|
and not ndef1.groups.unbreakable
|
||||||
@ -282,8 +227,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||||||
node1 = node_ok(p1, "air").name
|
node1 = node_ok(p1, "air").name
|
||||||
ndef1 = minetest.registered_nodes[node1]
|
ndef1 = minetest.registered_nodes[node1]
|
||||||
|
|
||||||
if node1 ~= "air"
|
if node1 ~= "air" and node1 ~= "ignore"
|
||||||
and node1 ~= "ignore"
|
|
||||||
and ndef1
|
and ndef1
|
||||||
and not ndef1.groups.level
|
and not ndef1.groups.level
|
||||||
and not ndef1.groups.unbreakable
|
and not ndef1.groups.unbreakable
|
||||||
@ -317,28 +261,19 @@ end
|
|||||||
|
|
||||||
-- specific attacks
|
-- specific attacks
|
||||||
local specific_attack = function(list, what)
|
local specific_attack = function(list, what)
|
||||||
|
|
||||||
-- no list so attack default (player, animals etc.)
|
-- no list so attack default (player, animals etc.)
|
||||||
if list == nil then
|
if list == nil then return true end
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- found entity on list to attack?
|
-- found entity on list to attack?
|
||||||
for no = 1, #list do
|
for no = 1, #list do
|
||||||
|
if list[no] == what then return true end
|
||||||
if list[no] == what then
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- find someone to attack
|
-- find someone to attack
|
||||||
function mob_class:monster_attack()
|
function mob_class:monster_attack()
|
||||||
if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then
|
if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local s = self.object:get_pos()
|
local s = self.object:get_pos()
|
||||||
local p, sp, dist
|
local p, sp, dist
|
||||||
@ -392,7 +327,7 @@ function mob_class:monster_attack()
|
|||||||
p = player:get_pos()
|
p = player:get_pos()
|
||||||
sp = s
|
sp = s
|
||||||
|
|
||||||
dist = vector.distance(p, s)
|
dist = vector_distance(p, s)
|
||||||
|
|
||||||
-- aim higher to make looking up hills more realistic
|
-- aim higher to make looking up hills more realistic
|
||||||
p.y = p.y + 1
|
p.y = p.y + 1
|
||||||
@ -414,7 +349,7 @@ function mob_class:monster_attack()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not min_player and #blacklist_attack > 0 then
|
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
|
end
|
||||||
-- attack player
|
-- attack player
|
||||||
if min_player then
|
if min_player then
|
||||||
@ -425,7 +360,6 @@ end
|
|||||||
|
|
||||||
-- npc, find closest monster to attack
|
-- npc, find closest monster to attack
|
||||||
function mob_class:npc_attack()
|
function mob_class:npc_attack()
|
||||||
|
|
||||||
if self.type ~= "npc"
|
if self.type ~= "npc"
|
||||||
or not self.attacks_monsters
|
or not self.attacks_monsters
|
||||||
or self.state == "attack" then
|
or self.state == "attack" then
|
||||||
@ -444,7 +378,7 @@ function mob_class:npc_attack()
|
|||||||
p = obj.object:get_pos()
|
p = obj.object:get_pos()
|
||||||
sp = s
|
sp = s
|
||||||
|
|
||||||
local dist = vector.distance(p, s)
|
local dist = vector_distance(p, s)
|
||||||
|
|
||||||
-- aim higher to make looking up hills more realistic
|
-- aim higher to make looking up hills more realistic
|
||||||
p.y = p.y + 1
|
p.y = p.y + 1
|
||||||
@ -466,19 +400,13 @@ end
|
|||||||
|
|
||||||
-- dogshoot attack switch and counter function
|
-- dogshoot attack switch and counter function
|
||||||
function mob_class:dogswitch(dtime)
|
function mob_class:dogswitch(dtime)
|
||||||
|
|
||||||
-- switch mode not activated
|
-- switch mode not activated
|
||||||
if not self.dogshoot_switch
|
if not self.dogshoot_switch or not dtime then return 0 end
|
||||||
or not dtime then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
self.dogshoot_count = self.dogshoot_count + dtime
|
self.dogshoot_count = self.dogshoot_count + dtime
|
||||||
|
|
||||||
if (self.dogshoot_switch == 1
|
if (self.dogshoot_switch == 1 and self.dogshoot_count > self.dogshoot_count_max)
|
||||||
and self.dogshoot_count > self.dogshoot_count_max)
|
or (self.dogshoot_switch == 2 and self.dogshoot_count > self.dogshoot_count2_max) then
|
||||||
or (self.dogshoot_switch == 2
|
|
||||||
and self.dogshoot_count > self.dogshoot_count2_max) then
|
|
||||||
|
|
||||||
self.dogshoot_count = 0
|
self.dogshoot_count = 0
|
||||||
|
|
||||||
@ -525,13 +453,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
|
|
||||||
if is_player then
|
if is_player then
|
||||||
-- is mob out of reach?
|
-- is mob out of reach?
|
||||||
if vector.distance(mob_pos, player_pos) > 3 then
|
if vector_distance(mob_pos, player_pos) > 3 then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
-- is mob protected?
|
-- is mob protected?
|
||||||
if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then
|
if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_potions.update_haste_and_fatigue(hitter)
|
mcl_potions.update_haste_and_fatigue(hitter)
|
||||||
end
|
end
|
||||||
@ -540,13 +464,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
local time_diff = time_now - self.invul_timestamp
|
local time_diff = time_now - self.invul_timestamp
|
||||||
|
|
||||||
-- check for invulnerability time in microseconds (0.5 second)
|
-- check for invulnerability time in microseconds (0.5 second)
|
||||||
if time_diff <= 500000 and time_diff >= 0 then
|
if time_diff <= 500000 and time_diff >= 0 then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- custom punch function
|
-- custom punch function
|
||||||
if self.do_punch then
|
if self.do_punch then
|
||||||
|
|
||||||
-- when false skip going any further
|
-- when false skip going any further
|
||||||
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
||||||
return
|
return
|
||||||
@ -562,15 +483,11 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
local time_now = minetest.get_us_time()
|
local time_now = minetest.get_us_time()
|
||||||
|
|
||||||
if is_player then
|
if is_player then
|
||||||
if minetest.is_creative_enabled(hitter:get_player_name()) then
|
if minetest.is_creative_enabled(hitter:get_player_name()) then self.health = 0 end
|
||||||
self.health = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- set/update 'drop xp' timestamp if hitted by player
|
-- set/update 'drop xp' timestamp if hitted by player
|
||||||
self.xp_timestamp = time_now
|
self.xp_timestamp = time_now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- punch interval
|
-- punch interval
|
||||||
local weapon = hitter:get_wielded_item()
|
local weapon = hitter:get_wielded_item()
|
||||||
local punch_interval = 1.4
|
local punch_interval = 1.4
|
||||||
@ -591,18 +508,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
end
|
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)
|
tmp = tflp / (tool_capabilities.full_punch_interval or 1.4)
|
||||||
|
tmp = tmp < 0 and 0 or (tmp > 1 and 1 or tmp)
|
||||||
if tmp < 0 then
|
damage = damage + (tool_capabilities.damage_groups[group] or 0) * tmp * ((armor[group] or 0) / 100.0)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- strength and weakness effects
|
-- strength and weakness effects
|
||||||
@ -621,9 +530,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
|
|
||||||
-- check for tool immunity or special damage
|
-- check for tool immunity or special damage
|
||||||
for n = 1, #self.immune_to do
|
for n = 1, #self.immune_to do
|
||||||
|
|
||||||
if self.immune_to[n][1] == weapon:get_name() then
|
if self.immune_to[n][1] == weapon:get_name() then
|
||||||
|
|
||||||
damage = self.immune_to[n][2] or 0
|
damage = self.immune_to[n][2] or 0
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@ -631,7 +538,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
|
|
||||||
-- healing
|
-- healing
|
||||||
if damage <= -1 then
|
if damage <= -1 then
|
||||||
self.health = self.health - math.floor(damage)
|
self.health = self.health - floor(damage)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -651,7 +558,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
local weapon = hitter:get_wielded_item(player)
|
local weapon = hitter:get_wielded_item(player)
|
||||||
local def = weapon:get_definition()
|
local def = weapon:get_definition()
|
||||||
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
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)
|
weapon:add_wear(wear)
|
||||||
tt.reload_itemstack_description(weapon) -- update tooltip
|
tt.reload_itemstack_description(weapon) -- update tooltip
|
||||||
hitter:set_wielded_item(weapon)
|
hitter:set_wielded_item(weapon)
|
||||||
@ -662,14 +569,12 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
|
|
||||||
local die = false
|
local die = false
|
||||||
|
|
||||||
|
|
||||||
if damage >= 0 then
|
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.
|
-- 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
|
if damage >= 0.1 then
|
||||||
-- weapon sounds
|
-- weapon sounds
|
||||||
if weapon:get_definition().sounds ~= nil then
|
if weapon:get_definition().sounds ~= nil then
|
||||||
|
local s = random(0, #weapon:get_definition().sounds)
|
||||||
local s = math.random(0, #weapon:get_definition().sounds)
|
|
||||||
|
|
||||||
minetest.sound_play(weapon:get_definition().sounds[s], {
|
minetest.sound_play(weapon:get_definition().sounds[s], {
|
||||||
object = self.object, --hitter,
|
object = self.object, --hitter,
|
||||||
@ -696,27 +601,20 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- knock back effect (only on full punch)
|
-- knock back effect (only on full punch)
|
||||||
if self.knock_back
|
if self.knock_back and tflp >= punch_interval then
|
||||||
and tflp >= punch_interval then
|
|
||||||
-- direction error check
|
-- direction error check
|
||||||
dir = dir or {x = 0, y = 0, z = 0}
|
dir = dir or vector_zero()
|
||||||
|
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
if not v then return end
|
if not v then return end
|
||||||
local r = 1.4 - math.min(punch_interval, 1.4)
|
local r = 1.4 - min(punch_interval, 1.4)
|
||||||
local kb = r * (math.abs(v.x)+math.abs(v.z))
|
local kb = r * (abs(v.x)+abs(v.z))
|
||||||
local up = 2.625
|
local up = 2.625
|
||||||
|
|
||||||
if die==true then
|
if die then kb = kb * 1.25 end
|
||||||
kb=kb*1.25
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if already in air then dont go up anymore when hit
|
-- if already in air then dont go up anymore when hit
|
||||||
if math.abs(v.y) > 0.1
|
if abs(v.y) > 0.1 or self.fly then up = 0 end
|
||||||
or self.fly then
|
|
||||||
up = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- check if tool already has specific knockback value
|
-- check if tool already has specific knockback value
|
||||||
if tool_capabilities.damage_groups["knockback"] then
|
if tool_capabilities.damage_groups["knockback"] then
|
||||||
@ -725,21 +623,17 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
kb = kb * 1.25
|
kb = kb * 1.25
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local luaentity = hitter and hitter:get_luaentity()
|
||||||
local luaentity
|
|
||||||
if hitter then
|
|
||||||
luaentity = hitter:get_luaentity()
|
|
||||||
end
|
|
||||||
if hitter and is_player then
|
if hitter and is_player then
|
||||||
local wielditem = hitter:get_wielded_item()
|
local wielditem = hitter:get_wielded_item()
|
||||||
kb = kb + 9 * mcl_enchanting.get_enchantment(wielditem, "knockback")
|
kb = kb + 9 * mcl_enchanting.get_enchantment(wielditem, "knockback")
|
||||||
-- add player velocity to mob knockback
|
-- add player velocity to mob knockback
|
||||||
local hv = hitter:get_velocity()
|
local hv = hitter:get_velocity()
|
||||||
local dir_dot = (hv.x * dir.x) + (hv.z * dir.z)
|
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 player_mag = ((hv.x * hv.x) + (hv.z * hv.z))^0.5
|
||||||
local mob_mag = math.sqrt((v.x * v.x) + (v.z * v.z))
|
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
|
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
|
end
|
||||||
elseif luaentity and luaentity._knockback and die == false then
|
elseif luaentity and luaentity._knockback and die == false then
|
||||||
kb = kb + luaentity._knockback
|
kb = kb + luaentity._knockback
|
||||||
@ -747,12 +641,12 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
kb = kb + luaentity._knockback * 0.25
|
kb = kb + luaentity._knockback * 0.25
|
||||||
end
|
end
|
||||||
self._kb_turn = true
|
self._kb_turn = true
|
||||||
self._turn_to=self.object:get_yaw()-1.57
|
self:turn_by(HALFPI, .1) -- knockback turn
|
||||||
self.frame_speed_multiplier=2.3
|
self.frame_speed_multiplier=2.3
|
||||||
if self.animation.run_end then
|
if self.animation.run_end then
|
||||||
self:set_animation( "run")
|
self:set_animation("run")
|
||||||
elseif self.animation.walk_end then
|
elseif self.animation.walk_end then
|
||||||
self:set_animation( "walk")
|
self:set_animation("walk")
|
||||||
end
|
end
|
||||||
minetest.after(0.2, function()
|
minetest.after(0.2, function()
|
||||||
if self and self.object then
|
if self and self.object then
|
||||||
@ -760,11 +654,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
self._kb_turn = false
|
self._kb_turn = false
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
self.object:add_velocity({
|
self.object:add_velocity(vector_new(dir.x * kb, up*2, dir.z * kb ))
|
||||||
x = dir.x * kb,
|
|
||||||
y = up*2,
|
|
||||||
z = dir.z * kb
|
|
||||||
})
|
|
||||||
|
|
||||||
self.pause_timer = 0.25
|
self.pause_timer = 0.25
|
||||||
end
|
end
|
||||||
@ -772,12 +662,15 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
|
|
||||||
-- if skittish then run away
|
-- if skittish then run away
|
||||||
if hitter and is_player and hitter:get_pos() and not die and self.runaway == true and self.state ~= "flop" then
|
if hitter and is_player and hitter:get_pos() and not die and self.runaway == true and self.state ~= "flop" then
|
||||||
|
local hp, sp = hitter:get_pos(), self.object:get_pos()
|
||||||
local yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
self:turn_in_direction(sp.x - hp.x, sp.z - hp.z, 1)
|
||||||
minetest.after(0.2,function()
|
minetest.after(0.2,function()
|
||||||
if self and self.object and self.object:get_pos() and hitter and is_player and hitter:get_pos() then
|
if self and self.object and hitter and is_player then
|
||||||
yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
local hp, sp = hitter:get_pos(), self.object:get_pos()
|
||||||
self:set_velocity( self.run_velocity)
|
if hp and sp then
|
||||||
|
self:turn_in_direction(sp.x - hp.x, sp.z - hp.z, 1)
|
||||||
|
self:set_velocity(self.run_velocity)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
self.state = "runaway"
|
self.state = "runaway"
|
||||||
@ -808,7 +701,6 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||||||
local obj = nil
|
local obj = nil
|
||||||
|
|
||||||
for n = 1, #objs do
|
for n = 1, #objs do
|
||||||
|
|
||||||
obj = objs[n]:get_luaentity()
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
if obj then
|
if obj then
|
||||||
@ -840,11 +732,7 @@ end
|
|||||||
|
|
||||||
function mob_class:check_aggro(dtime)
|
function mob_class:check_aggro(dtime)
|
||||||
if not self._aggro or not self.attack then return end
|
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
|
if self._check_aggro_timer > 5 then
|
||||||
self._check_aggro_timer = 0
|
self._check_aggro_timer = 0
|
||||||
|
|
||||||
@ -852,7 +740,7 @@ function mob_class:check_aggro(dtime)
|
|||||||
-- TODO consider removing this in favour of what is done in do_states_attack
|
-- 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
|
-- 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?
|
-- 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._aggro = nil
|
||||||
self.attack = nil
|
self.attack = nil
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
@ -878,17 +766,14 @@ local function clear_aggro(self)
|
|||||||
self.path.way = nil
|
self.path.way = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:do_states_attack (dtime)
|
function mob_class:do_states_attack(dtime)
|
||||||
self.timer = self.timer + dtime
|
self.timer = self.timer + dtime
|
||||||
if self.timer > 100 then
|
if self.timer > 100 then self.timer = 1 end
|
||||||
self.timer = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local s = self.object:get_pos()
|
local s = self.object:get_pos()
|
||||||
if not s then return end
|
if not s then return end
|
||||||
|
|
||||||
local p = self.attack:get_pos() or s
|
local p = self.attack:get_pos() or s
|
||||||
|
|
||||||
local yaw = self.object:get_yaw() or 0
|
local yaw = self.object:get_yaw() or 0
|
||||||
|
|
||||||
-- stop attacking if player invisible or out of range
|
-- stop attacking if player invisible or out of range
|
||||||
@ -920,20 +805,15 @@ function mob_class:do_states_attack (dtime)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- calculate distance from mob and enemy
|
-- 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 self.attack_type == "explode" then
|
||||||
|
|
||||||
if target_line_of_sight then
|
if target_line_of_sight then
|
||||||
local vec = { x = p.x - s.x, z = p.z - s.z }
|
self:turn_in_direction(p.x - s.x, p.z - s.z, 1)
|
||||||
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
|
||||||
if p.x > s.x then yaw = yaw +math.pi end
|
|
||||||
yaw = self:set_yaw( yaw, 0, dtime)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local node_break_radius = self.explosion_radius or 1
|
local node_break_radius = self.explosion_radius or 1
|
||||||
local entity_damage_radius = self.explosion_damage_radius
|
local entity_damage_radius = self.explosion_damage_radius or (node_break_radius * 2)
|
||||||
or (node_break_radius * 2)
|
|
||||||
|
|
||||||
-- start timer when in reach and line of sight
|
-- start timer when in reach and line of sight
|
||||||
if not self.v_start and dist <= self.reach and target_line_of_sight then
|
if not self.v_start and dist <= self.reach and target_line_of_sight then
|
||||||
@ -960,9 +840,9 @@ function mob_class:do_states_attack (dtime)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.animation and self.animation.run_start then
|
if self.animation and self.animation.run_start then
|
||||||
self:set_animation( "run")
|
self:set_animation("run")
|
||||||
else
|
else
|
||||||
self:set_animation( "walk")
|
self:set_animation("walk")
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.v_start then
|
if self.v_start then
|
||||||
@ -1005,92 +885,50 @@ 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 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
|
or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then
|
||||||
|
|
||||||
if self.fly
|
if self.fly and dist > self.reach then
|
||||||
and dist > self.reach then
|
local p1, p2 = s, p
|
||||||
|
local me_y, p_y = floor(p1.y), floor(p2.y + 1)
|
||||||
local p1 = s
|
|
||||||
local me_y = math.floor(p1.y)
|
|
||||||
local p2 = p
|
|
||||||
local p_y = math.floor(p2.y + 1)
|
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
|
|
||||||
if self:flight_check( s) then
|
if self:flight_check( s) then
|
||||||
|
|
||||||
if me_y < p_y then
|
if me_y < p_y then
|
||||||
|
self.object:set_velocity(vector_new(v.x, 1 * self.walk_velocity, v.z))
|
||||||
self.object:set_velocity({
|
|
||||||
x = v.x,
|
|
||||||
y = 1 * self.walk_velocity,
|
|
||||||
z = v.z
|
|
||||||
})
|
|
||||||
|
|
||||||
elseif me_y > p_y then
|
elseif me_y > p_y then
|
||||||
|
self.object:set_velocity(vector_new(v.x, -1 * self.walk_velocity, v.z))
|
||||||
self.object:set_velocity({
|
|
||||||
x = v.x,
|
|
||||||
y = -1 * self.walk_velocity,
|
|
||||||
z = v.z
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if me_y < p_y then
|
if me_y < p_y then
|
||||||
|
self.object:set_velocity(vector_new(v.x, 0.01, v.z))
|
||||||
self.object:set_velocity({
|
|
||||||
x = v.x,
|
|
||||||
y = 0.01,
|
|
||||||
z = v.z
|
|
||||||
})
|
|
||||||
|
|
||||||
elseif me_y > p_y then
|
elseif me_y > p_y then
|
||||||
|
self.object:set_velocity(vector_new(v.x, -0.01, v.z))
|
||||||
self.object:set_velocity({
|
|
||||||
x = v.x,
|
|
||||||
y = -0.01,
|
|
||||||
z = v.z
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- rnd: new movement direction
|
-- rnd: new movement direction
|
||||||
if self.path.following
|
if self.path.following and self.path.way and self.attack_type ~= "dogshoot" then
|
||||||
and self.path.way
|
|
||||||
and self.attack_type ~= "dogshoot" then
|
|
||||||
|
|
||||||
-- no paths longer than 50
|
-- no paths longer than 50
|
||||||
if #self.path.way > 50
|
if #self.path.way > 50 or dist < self.reach then
|
||||||
or dist < self.reach then
|
|
||||||
self.path.following = false
|
self.path.following = false
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local p1 = self.path.way[1]
|
local p1 = self.path.way[1]
|
||||||
|
|
||||||
if not p1 then
|
if not p1 then
|
||||||
self.path.following = false
|
self.path.following = false
|
||||||
return
|
return
|
||||||
end
|
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
|
-- reached waypoint, remove it from queue
|
||||||
table.remove(self.path.way, 1)
|
table.remove(self.path.way, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set new temporary target
|
-- set new temporary target
|
||||||
p = {x = p1.x, y = p1.y, z = p1.z}
|
p = vector_copy(p1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local vec = {
|
self:turn_in_direction(p.x - s.x, p.z - s.z, 10)
|
||||||
x = p.x - s.x,
|
|
||||||
z = p.z - s.z
|
|
||||||
}
|
|
||||||
|
|
||||||
yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate
|
|
||||||
|
|
||||||
if p.x > s.x then yaw = yaw + math.pi end
|
|
||||||
|
|
||||||
yaw = self:set_yaw( yaw, 0, dtime)
|
|
||||||
|
|
||||||
-- move towards enemy if beyond mob reach
|
-- move towards enemy if beyond mob reach
|
||||||
if dist > self.reach then
|
if dist > self.reach then
|
||||||
@ -1100,10 +938,9 @@ function mob_class:do_states_attack (dtime)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self:is_at_cliff_or_danger() then
|
if self:is_at_cliff_or_danger() then
|
||||||
self:set_velocity( 0)
|
self:set_velocity(0)
|
||||||
self:set_animation( "stand")
|
self:set_animation("stand")
|
||||||
local yaw = self.object:get_yaw() or 0
|
--self:turn_by(PI * (random() - 0.5), 10)
|
||||||
yaw = self:set_yaw( yaw + 0.78, 8)
|
|
||||||
else
|
else
|
||||||
if self.path.stuck then
|
if self.path.stuck then
|
||||||
self:set_velocity(self.walk_velocity)
|
self:set_velocity(self.walk_velocity)
|
||||||
@ -1129,19 +966,13 @@ function mob_class:do_states_attack (dtime)
|
|||||||
self.timer = 0
|
self.timer = 0
|
||||||
|
|
||||||
if not self.custom_attack then
|
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")
|
self:set_animation("punch2")
|
||||||
else
|
else
|
||||||
self:set_animation("punch")
|
self:set_animation("punch")
|
||||||
end
|
end
|
||||||
|
|
||||||
local p2 = p
|
if self:line_of_sight(vector_offset(p, 0, .5, 0), vector_offset(s, 0, .5, 0)) == true then
|
||||||
local s2 = s
|
|
||||||
|
|
||||||
p2.y = p2.y + .5
|
|
||||||
s2.y = s2.y + .5
|
|
||||||
|
|
||||||
if self:line_of_sight( p2, s2) == true then
|
|
||||||
self:mob_sound("attack")
|
self:mob_sound("attack")
|
||||||
|
|
||||||
-- punch player (or what player is attached to)
|
-- punch player (or what player is attached to)
|
||||||
@ -1167,59 +998,31 @@ function mob_class:do_states_attack (dtime)
|
|||||||
elseif self.attack_type == "shoot"
|
elseif self.attack_type == "shoot"
|
||||||
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1)
|
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
|
or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then
|
||||||
|
local vec = vector_new(p.x - s.x, p.y - s.y - 1, p.z - s.z)
|
||||||
p.y = p.y - .5
|
local dist = (vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)^0.5
|
||||||
s.y = s.y + .5
|
self:turn_in_direction(vec.x, vec.z, 10)
|
||||||
|
|
||||||
local dist = vector.distance(p, s)
|
|
||||||
local vec = {
|
|
||||||
x = p.x - s.x,
|
|
||||||
y = p.y - s.y,
|
|
||||||
z = p.z - s.z
|
|
||||||
}
|
|
||||||
|
|
||||||
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
|
||||||
|
|
||||||
if p.x > s.x then yaw = yaw +math.pi end
|
|
||||||
|
|
||||||
yaw = self:set_yaw( yaw, 0, 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 self.strafes then
|
||||||
if not self.strafe_direction then
|
if not self.strafe_direction then self.strafe_direction = HALFPI end
|
||||||
self.strafe_direction = 1.57
|
if random(40) == 1 then self.strafe_direction = self.strafe_direction * -1 end
|
||||||
end
|
|
||||||
if math.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 dir = -atan2(p.x - s.x, p.z - s.z)
|
||||||
local dir2 = vector.multiply(dir, 0.3 * self.walk_velocity)
|
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 dir2 and stay_away_from_player then
|
if self.avoid_distance and dist < self.avoid_distance and self.shooter_avoid_enemy then
|
||||||
self.acc = vector.add(dir2, stay_away_from_player)
|
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
|
end
|
||||||
else
|
else
|
||||||
self:set_velocity( 0)
|
self:set_velocity(0)
|
||||||
|
self:set_animation("stand")
|
||||||
end
|
end
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2
|
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) * 0.5
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
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.timer = 0
|
||||||
self:set_animation( "shoot")
|
self:set_animation( "shoot")
|
||||||
|
|
||||||
@ -1228,7 +1031,6 @@ function mob_class:do_states_attack (dtime)
|
|||||||
|
|
||||||
-- Shoot arrow
|
-- Shoot arrow
|
||||||
if minetest.registered_entities[self.arrow] then
|
if minetest.registered_entities[self.arrow] then
|
||||||
|
|
||||||
local arrow, ent
|
local arrow, ent
|
||||||
local v = 1
|
local v = 1
|
||||||
if not self.shoot_arrow then
|
if not self.shoot_arrow then
|
||||||
@ -1238,9 +1040,7 @@ function mob_class:do_states_attack (dtime)
|
|||||||
end)
|
end)
|
||||||
arrow = minetest.add_entity(p, self.arrow)
|
arrow = minetest.add_entity(p, self.arrow)
|
||||||
ent = arrow:get_luaentity()
|
ent = arrow:get_luaentity()
|
||||||
if ent.velocity then
|
v = ent.velocity or v
|
||||||
v = ent.velocity
|
|
||||||
end
|
|
||||||
ent.switch = 1
|
ent.switch = 1
|
||||||
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
|
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
|
||||||
|
|
||||||
@ -1252,12 +1052,9 @@ function mob_class:do_states_attack (dtime)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5
|
|
||||||
-- offset makes shoot aim accurate
|
-- offset makes shoot aim accurate
|
||||||
vec.y = vec.y + self.shoot_offset
|
vec.y = vec.y + self.shoot_offset
|
||||||
vec.x = vec.x * (v / amount)
|
vec.x, vec.y, vec.z = vec.x * (v / dist), vec.y * (v / dist), vec.z * (v / dist)
|
||||||
vec.y = vec.y * (v / amount)
|
|
||||||
vec.z = vec.z * (v / amount)
|
|
||||||
if self.shoot_arrow then
|
if self.shoot_arrow then
|
||||||
vec = vector.normalize(vec)
|
vec = vector.normalize(vec)
|
||||||
self:shoot_arrow(p, vec)
|
self:shoot_arrow(p, vec)
|
||||||
@ -1266,13 +1063,9 @@ function mob_class:do_states_attack (dtime)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif self.attack_type == "custom" and self.attack_state then
|
elseif self.attack_type == "custom" and self.attack_state then
|
||||||
self.attack_state(self, dtime)
|
self.attack_state(self, dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.on_attack then
|
if self.on_attack then self.on_attack(self, dtime) end
|
||||||
self.on_attack(self, dtime)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -250,9 +250,7 @@ end
|
|||||||
|
|
||||||
-- set defined animation
|
-- set defined animation
|
||||||
function mob_class:set_animation(anim, fixed_frame)
|
function mob_class:set_animation(anim, fixed_frame)
|
||||||
if not self.animation or not anim then
|
if not self.animation or not anim then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.jockey and self.object:get_attach() then
|
if self.jockey and self.object:get_attach() then
|
||||||
anim = "jockey"
|
anim = "jockey"
|
||||||
@ -260,11 +258,7 @@ function mob_class:set_animation(anim, fixed_frame)
|
|||||||
self.jockey = nil
|
self.jockey = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.state == "die" and anim ~= "die" and anim ~= "stand" then
|
if self.state == "die" and anim ~= "die" and anim ~= "stand" then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if self.fly and self:flight_check() and anim == "walk" then anim = "fly" end
|
if self.fly and self:flight_check() and anim == "walk" then anim = "fly" end
|
||||||
|
|
||||||
@ -279,12 +273,7 @@ function mob_class:set_animation(anim, fixed_frame)
|
|||||||
self._current_animation = anim
|
self._current_animation = anim
|
||||||
|
|
||||||
local a_start = self.animation[anim .. "_start"]
|
local a_start = self.animation[anim .. "_start"]
|
||||||
local a_end
|
local a_end = fixed_frame and a_start or self.animation[anim .. "_end"]
|
||||||
if fixed_frame then
|
|
||||||
a_end = a_start
|
|
||||||
else
|
|
||||||
a_end = self.animation[anim .. "_end"]
|
|
||||||
end
|
|
||||||
if a_start and a_end then
|
if a_start and a_end then
|
||||||
self.object:set_animation({
|
self.object:set_animation({
|
||||||
x = a_start,
|
x = a_start,
|
||||||
@ -294,11 +283,6 @@ function mob_class:set_animation(anim, fixed_frame)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- above function exported for mount.lua
|
|
||||||
function mcl_mobs:set_animation(self, anim)
|
|
||||||
self:set_animation(anim)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function who_are_you_looking_at (self, dtime)
|
local function who_are_you_looking_at (self, dtime)
|
||||||
if self.order == "sleep" then
|
if self.order == "sleep" then
|
||||||
self._locked_object = nil
|
self._locked_object = nil
|
||||||
|
@ -6,6 +6,19 @@ local modname = minetest.get_current_modname()
|
|||||||
local path = minetest.get_modpath(modname)
|
local path = minetest.get_modpath(modname)
|
||||||
local S = minetest.get_translator(modname)
|
local S = minetest.get_translator(modname)
|
||||||
mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
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
|
--api and helpers
|
||||||
-- effects: sounds and particles mostly
|
-- effects: sounds and particles mostly
|
||||||
dofile(path .. "/effects.lua")
|
dofile(path .. "/effects.lua")
|
||||||
@ -19,10 +32,9 @@ dofile(path .. "/items.lua")
|
|||||||
dofile(path .. "/pathfinding.lua")
|
dofile(path .. "/pathfinding.lua")
|
||||||
-- combat: attack logic
|
-- combat: attack logic
|
||||||
dofile(path .. "/combat.lua")
|
dofile(path .. "/combat.lua")
|
||||||
-- the enity functions themselves
|
-- the entity functions themselves
|
||||||
dofile(path .. "/api.lua")
|
dofile(path .. "/api.lua")
|
||||||
|
|
||||||
|
|
||||||
--utility functions
|
--utility functions
|
||||||
dofile(path .. "/breeding.lua")
|
dofile(path .. "/breeding.lua")
|
||||||
dofile(path .. "/spawning.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 extended_pet_control = minetest.settings:get_bool("mcl_extended_pet_control",true)
|
||||||
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
|
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
|
--#### REGISTER FUNCS
|
||||||
|
|
||||||
-- Code to execute before custom on_rightclick handling
|
-- 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.spawning_mobs[name] = true
|
||||||
mcl_mobs.registered_mobs[name] = def
|
mcl_mobs.registered_mobs[name] = def
|
||||||
|
|
||||||
local can_despawn
|
local can_despawn = def.can_despawn
|
||||||
if def.can_despawn ~= nil then
|
if def.can_despawn == nil then can_despawn = def.spawn_class ~= "passive" end
|
||||||
can_despawn = def.can_despawn
|
|
||||||
elseif def.spawn_class == "passive" then
|
|
||||||
can_despawn = false
|
|
||||||
else
|
|
||||||
can_despawn = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scale_difficulty(value, default, min, special)
|
local function scale_difficulty(value, default, min, special)
|
||||||
if (not value) or (value == default) or (value == special) then
|
if (not value) or (value == default) or (value == special) then
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name = mcl_mobs
|
name = mcl_mobs
|
||||||
author = PilzAdam
|
author = PilzAdam, kno10
|
||||||
description = Adds a mob API for mods to add animals or monsters, etc.
|
description = Adds a mob API for mods to add animals or monsters, etc.
|
||||||
depends = mcl_particles, mcl_luck
|
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
|
optional_depends = mcl_weather, mcl_explosions, mcl_hunger, mcl_worlds, invisibility, lucky_block, cmi, doc_identifier, mcl_armor, mcl_portals, mcl_experience, mcl_sculk
|
||||||
|
@ -1,110 +1,41 @@
|
|||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
local mob_class = mcl_mobs.mob_class
|
local mob_class = mcl_mobs.mob_class
|
||||||
-- lib_mount by Blert2112 (edited by TenPlus1)
|
-- based on lib_mount by Blert2112 (edited by TenPlus1)
|
||||||
|
|
||||||
local enable_crash = false
|
local enable_crash = false
|
||||||
local crash_threshold = 6.5 -- ignored if enable_crash=false
|
local crash_threshold = 6.5 -- ignored if enable_crash=false
|
||||||
|
local GRAVITY = -9.8
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
local node_ok = mcl_mobs.node_ok
|
||||||
|
local sign = math.sign -- minetest extension
|
||||||
--
|
|
||||||
-- Helper functions
|
|
||||||
--
|
|
||||||
|
|
||||||
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 {name = fallback}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function node_is(pos)
|
local function node_is(pos)
|
||||||
|
|
||||||
local node = node_ok(pos)
|
local node = node_ok(pos)
|
||||||
|
if node.name == "air" then return "air" end
|
||||||
if node.name == "air" then
|
local ndef = minetest.registered_nodes[node.name]
|
||||||
return "air"
|
if not ndef then return "other" end -- unknown/ignore
|
||||||
end
|
if ndef.groups.lava then return "lava" end
|
||||||
|
if ndef.groups.liquid then return "liquid" end
|
||||||
if minetest.get_item_group(node.name, "lava") ~= 0 then
|
if ndef.walkable then return "walkable" end
|
||||||
return "lava"
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.get_item_group(node.name, "liquid") ~= 0 then
|
|
||||||
return "liquid"
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.registered_nodes[node.name].walkable == true then
|
|
||||||
return "walkable"
|
|
||||||
end
|
|
||||||
|
|
||||||
return "other"
|
return "other"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function get_sign(i)
|
|
||||||
|
|
||||||
i = i or 0
|
|
||||||
|
|
||||||
if i == 0 then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return i / math.abs(i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_velocity(v, yaw, y)
|
|
||||||
|
|
||||||
local x = -math.sin(yaw) * v
|
|
||||||
local z = math.cos(yaw) * v
|
|
||||||
|
|
||||||
return {x = x, y = y, z = z}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_v(v)
|
|
||||||
return math.sqrt(v.x * v.x + v.z * v.z)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function force_detach(player)
|
local function force_detach(player)
|
||||||
|
|
||||||
local attached_to = player:get_attach()
|
local attached_to = player:get_attach()
|
||||||
|
if not attached_to then return end
|
||||||
if not attached_to then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local entity = attached_to:get_luaentity()
|
local entity = attached_to:get_luaentity()
|
||||||
|
if entity.driver and entity.driver == player then entity.driver = nil end
|
||||||
if entity.driver
|
|
||||||
and entity.driver == player then
|
|
||||||
|
|
||||||
entity.driver = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
player:set_detach()
|
player:set_detach()
|
||||||
mcl_player.player_attached[player:get_player_name()] = false
|
mcl_player.player_attached[player:get_player_name()] = false
|
||||||
player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
player:set_eye_offset(vector.zero(), vector.zero())
|
||||||
mcl_player.player_set_animation(player, "stand" , 30)
|
mcl_player.player_set_animation(player, "stand" , 30)
|
||||||
player:set_properties({visual_size = {x = 1, y = 1} })
|
player:set_properties({visual_size = {x = 1, y = 1} })
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
minetest.register_on_leaveplayer(force_detach)
|
||||||
|
|
||||||
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
|
||||||
force_detach(player)
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_on_shutdown(function()
|
minetest.register_on_shutdown(function()
|
||||||
local players = minetest.get_connected_players()
|
local players = minetest.get_connected_players()
|
||||||
@ -118,39 +49,24 @@ minetest.register_on_dieplayer(function(player)
|
|||||||
return true
|
return true
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function mcl_mobs.attach(entity, player)
|
function mcl_mobs.attach(entity, player)
|
||||||
|
entity.player_rotation = entity.player_rotation or vector.zero()
|
||||||
local attach_at, eye_offset
|
entity.driver_attach_at = entity.driver_attach_at or vector.zero()
|
||||||
|
entity.driver_eye_offset = entity.driver_eye_offset or vector.zero()
|
||||||
entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
|
|
||||||
entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
|
|
||||||
entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
|
|
||||||
entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
|
entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
|
||||||
|
|
||||||
local rot_view = 0
|
local rot_view = entity.player_rotation.y == 90 and math.pi/2 or 0
|
||||||
|
local attach_at = entity.driver_attach_at
|
||||||
if entity.player_rotation.y == 90 then
|
local eye_offset = entity.driver_eye_offset
|
||||||
rot_view = math.pi/2
|
|
||||||
end
|
|
||||||
|
|
||||||
attach_at = entity.driver_attach_at
|
|
||||||
eye_offset = entity.driver_eye_offset
|
|
||||||
entity.driver = player
|
entity.driver = player
|
||||||
|
|
||||||
force_detach(player)
|
force_detach(player)
|
||||||
|
|
||||||
player:set_attach(entity.object, "", attach_at, entity.player_rotation)
|
player:set_attach(entity.object, "", attach_at, entity.player_rotation)
|
||||||
mcl_player.player_attached[player:get_player_name()] = true
|
mcl_player.player_attached[player:get_player_name()] = true
|
||||||
player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
|
player:set_eye_offset(eye_offset, vector.zero())
|
||||||
|
|
||||||
player:set_properties({
|
player:set_properties({ visual_size = entity.driver_scale })
|
||||||
visual_size = {
|
|
||||||
x = entity.driver_scale.x,
|
|
||||||
y = entity.driver_scale.y
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.after(0.2, function(name)
|
minetest.after(0.2, function(name)
|
||||||
local player = minetest.get_player_by_name(name)
|
local player = minetest.get_player_by_name(name)
|
||||||
@ -164,162 +80,88 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function mcl_mobs.detach(player, offset)
|
function mcl_mobs.detach(player, offset)
|
||||||
|
|
||||||
force_detach(player)
|
force_detach(player)
|
||||||
|
|
||||||
mcl_player.player_set_animation(player, "stand" , 30)
|
mcl_player.player_set_animation(player, "stand" , 30)
|
||||||
|
player:add_velocity(vector.new(math.random()*12-6,math.random()*3+5,math.random()*12-6)) --throw the rider off
|
||||||
--local pos = player:get_pos()
|
|
||||||
|
|
||||||
--pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
|
|
||||||
|
|
||||||
player:add_velocity(vector.new(math.random(-6,6),math.random(5,8),math.random(-6,6))) --throw the rider off
|
|
||||||
|
|
||||||
--[[
|
|
||||||
minetest.after(0.1, function(name, pos)
|
|
||||||
local player = minetest.get_player_by_name(name)
|
|
||||||
if player then
|
|
||||||
player:set_pos(pos)
|
|
||||||
end
|
|
||||||
end, player:get_player_name(), pos)
|
|
||||||
]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||||
|
|
||||||
local rot_view = 0
|
|
||||||
|
|
||||||
if entity.player_rotation.y == 90 then
|
|
||||||
rot_view = math.pi/2
|
|
||||||
end
|
|
||||||
|
|
||||||
local acce_y = 0
|
|
||||||
local velo = entity.object:get_velocity()
|
local velo = entity.object:get_velocity()
|
||||||
|
local v = math.sqrt(velo.x * velo.x + velo.y * velo.y)
|
||||||
entity.v = get_v(velo) * get_sign(entity.v)
|
local acce_y = GRAVITY
|
||||||
|
|
||||||
-- process controls
|
-- process controls
|
||||||
if entity.driver then
|
if entity.driver then
|
||||||
|
|
||||||
local ctrl = entity.driver:get_player_control()
|
local ctrl = entity.driver:get_player_control()
|
||||||
|
if ctrl.up then -- forward
|
||||||
-- move forwards
|
v = v + entity.accel * 0.1 * entity.run_velocity * 0.385
|
||||||
if ctrl.up then
|
elseif ctrl.down then -- backwards
|
||||||
|
if entity.max_speed_reverse == 0 and v == 0 then return end
|
||||||
entity.v = entity.v + entity.accel / 10 * entity.run_velocity / 2.6
|
v = v - entity.accel * 0.1 * entity.run_velocity * 0.385
|
||||||
|
|
||||||
-- move backwards
|
|
||||||
elseif ctrl.down then
|
|
||||||
|
|
||||||
if entity.max_speed_reverse == 0 and entity.v == 0 then
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
entity.v = entity.v - entity.accel / 10
|
entity:set_yaw(entity.driver:get_look_horizontal() - entity.rotate, 2)
|
||||||
end
|
|
||||||
|
|
||||||
-- fix mob rotation
|
|
||||||
entity.object:set_yaw(entity.driver:get_look_horizontal() - entity.rotate)
|
|
||||||
|
|
||||||
if can_fly then
|
if can_fly then
|
||||||
|
-- FIXME: use acce_y instead?
|
||||||
-- fly up
|
-- fly up
|
||||||
if ctrl.jump then
|
if ctrl.jump then
|
||||||
velo.y = velo.y + 1
|
velo.y = math.min(velo.y + 1, entity.accel)
|
||||||
if velo.y > entity.accel then velo.y = entity.accel end
|
elseif velo.y > 0.1 then
|
||||||
|
|
||||||
elseif velo.y > 0 then
|
|
||||||
velo.y = velo.y - 0.1
|
velo.y = velo.y - 0.1
|
||||||
if velo.y < 0 then velo.y = 0 end
|
elseif velo.y > 0 then
|
||||||
|
velo.y = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- fly down
|
-- fly down
|
||||||
if ctrl.sneak then
|
if ctrl.sneak then
|
||||||
velo.y = velo.y - 1
|
velo.y = math.max(velo.y - 1, -entity.accel)
|
||||||
if velo.y < -entity.accel then velo.y = -entity.accel end
|
elseif velo.y < -0.1 then
|
||||||
|
|
||||||
elseif velo.y < 0 then
|
|
||||||
velo.y = velo.y + 0.1
|
velo.y = velo.y + 0.1
|
||||||
if velo.y > 0 then velo.y = 0 end
|
elseif velo.y < 0 then
|
||||||
|
velo.y = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- jump
|
-- jump
|
||||||
if ctrl.jump then
|
if ctrl.jump then
|
||||||
|
|
||||||
if velo.y == 0 then
|
if velo.y == 0 then
|
||||||
velo.y = velo.y + entity.jump_height
|
velo.y = velo.y + entity.jump_height
|
||||||
acce_y = acce_y + (acce_y * 3) + 1
|
acce_y = acce_y + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
if math.abs(v) < 0.02 then -- stop
|
||||||
end
|
entity.object:set_velocity(vector.zero())
|
||||||
|
v = 0
|
||||||
-- Stop!
|
else
|
||||||
local s = get_sign(entity.v)
|
v = v - 0.02 * sign(v) -- slow down
|
||||||
|
|
||||||
entity.v = entity.v - 0.02 * s
|
|
||||||
|
|
||||||
if s ~= get_sign(entity.v) then
|
|
||||||
|
|
||||||
entity.object:set_velocity({x = 0, y = 0, z = 0})
|
|
||||||
entity.v = 0
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if not moving then set animation and return
|
-- if not moving then set animation and return
|
||||||
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
if v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||||
|
entity:set_animation(stand_anim)
|
||||||
if stand_anim then
|
|
||||||
mcl_mobs:set_animation(entity, stand_anim)
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
else
|
||||||
|
entity:set_animation(moving_anim)
|
||||||
-- set moving animation
|
|
||||||
if moving_anim then
|
|
||||||
mcl_mobs:set_animation(entity, moving_anim)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- enforce speed limit forward and reverse
|
-- enforce speed limit forward and reverse
|
||||||
local max_spd = entity.max_speed_reverse
|
v = math.max(-entity.max_speed_reverse, math.min(v, entity.max_speed_forward))
|
||||||
|
|
||||||
if get_sign(entity.v) >= 0 then
|
|
||||||
max_spd = entity.max_speed_forward
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.abs(entity.v) > max_spd then
|
|
||||||
entity.v = entity.v - get_sign(entity.v)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set position, velocity and acceleration
|
-- Set position, velocity and acceleration
|
||||||
local p = entity.object:get_pos()
|
local p = entity.object:get_pos()
|
||||||
local new_velo
|
|
||||||
local new_acce = {x = 0, y = -9.8, z = 0}
|
|
||||||
|
|
||||||
p.y = p.y - 0.5
|
p.y = p.y - 0.5
|
||||||
|
|
||||||
local ni = node_is(p)
|
local ni = node_is(p)
|
||||||
local v = entity.v
|
|
||||||
|
|
||||||
if ni == "air" then
|
if ni == "air" then
|
||||||
|
if can_fly then acce_y = acce_y - GRAVITY end
|
||||||
if can_fly == true then
|
|
||||||
new_acce.y = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif ni == "liquid" or ni == "lava" then
|
elseif ni == "liquid" or ni == "lava" then
|
||||||
|
|
||||||
if ni == "lava" and entity.lava_damage ~= 0 then
|
if ni == "lava" and entity.lava_damage ~= 0 then
|
||||||
|
|
||||||
entity.lava_counter = (entity.lava_counter or 0) + dtime
|
entity.lava_counter = (entity.lava_counter or 0) + dtime
|
||||||
|
|
||||||
if entity.lava_counter > 1 then
|
if entity.lava_counter > 1 then
|
||||||
|
|
||||||
minetest.sound_play("default_punch", {
|
minetest.sound_play("default_punch", {
|
||||||
object = entity.object,
|
object = entity.object,
|
||||||
max_hear_distance = 5
|
max_hear_distance = 5
|
||||||
@ -336,18 +178,15 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
|||||||
|
|
||||||
if entity.terrain_type == 2
|
if entity.terrain_type == 2
|
||||||
or entity.terrain_type == 3 then
|
or entity.terrain_type == 3 then
|
||||||
|
acce_y = 0
|
||||||
new_acce.y = 0
|
|
||||||
p.y = p.y + 1
|
p.y = p.y + 1
|
||||||
|
|
||||||
if node_is(p) == "liquid" then
|
if node_is(p) == "liquid" then
|
||||||
|
|
||||||
if velo.y >= 5 then
|
if velo.y >= 5 then
|
||||||
velo.y = 5
|
velo.y = 5
|
||||||
elseif velo.y < 0 then
|
elseif velo.y < 0 then
|
||||||
new_acce.y = 20
|
acce_y = 20
|
||||||
else
|
else
|
||||||
new_acce.y = 5
|
acce_y = 5
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if math.abs(velo.y) < 1 then
|
if math.abs(velo.y) < 1 then
|
||||||
@ -362,75 +201,51 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y)
|
local rot_view = entity.player_rotation.y == 90 and math.pi/2 or 0
|
||||||
new_acce.y = new_acce.y + acce_y
|
local new_yaw = entity.object:get_yaw() - rot_view
|
||||||
|
local new_velo = vector.new(-math.sin(new_yaw) * v, velo.y, math.cos(new_yaw) * v)
|
||||||
|
|
||||||
entity.object:set_velocity(new_velo)
|
entity.object:set_velocity(new_velo)
|
||||||
entity.object:set_acceleration(new_acce)
|
entity.object:set_acceleration(vector.new(0, acce_y, 0))
|
||||||
|
|
||||||
-- CRASH!
|
|
||||||
if enable_crash then
|
if enable_crash then
|
||||||
|
if v >= crash_threshold then
|
||||||
local intensity = entity.v2 - v
|
|
||||||
|
|
||||||
if intensity >= crash_threshold then
|
|
||||||
|
|
||||||
entity.object:punch(entity.object, 1.0, {
|
entity.object:punch(entity.object, 1.0, {
|
||||||
full_punch_interval = 1.0,
|
full_punch_interval = 1.0,
|
||||||
damage_groups = {fleshy = intensity}
|
damage_groups = {fleshy = v}
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
entity.v2 = v
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||||
|
|
||||||
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||||
|
|
||||||
local ctrl = entity.driver:get_player_control()
|
local ctrl = entity.driver:get_player_control()
|
||||||
local velo = entity.object:get_velocity()
|
local velo = entity.object:get_velocity()
|
||||||
local dir = entity.driver:get_look_dir()
|
local dir = entity.driver:get_look_dir()
|
||||||
local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
|
local yaw = entity.driver:get_look_horizontal()
|
||||||
|
|
||||||
if ctrl.up then
|
if ctrl.up then
|
||||||
entity.object:set_velocity({
|
entity.object:set_velocity(vector.new(dir.x * speed, dir.y * speed + 2, dir.z * speed))
|
||||||
x = dir.x * speed,
|
|
||||||
y = dir.y * speed + 2,
|
|
||||||
z = dir.z * speed
|
|
||||||
})
|
|
||||||
|
|
||||||
elseif ctrl.down then
|
elseif ctrl.down then
|
||||||
entity.object:set_velocity({
|
entity.object:set_velocity(vector.new(-dir.x * speed, dir.y * speed + 2, -dir.z * speed))
|
||||||
x = -dir.x * speed,
|
|
||||||
y = dir.y * speed + 2,
|
|
||||||
z = -dir.z * speed
|
|
||||||
})
|
|
||||||
|
|
||||||
elseif not ctrl.down or ctrl.up or ctrl.jump then
|
elseif not ctrl.down or ctrl.up or ctrl.jump then
|
||||||
entity.object:set_velocity({x = 0, y = -2, z = 0})
|
entity.object:set_velocity(vector.new(0, -2, 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
entity.object:set_yaw(yaw + math.pi + math.pi / 2 - entity.rotate)
|
entity:set_yaw(yaw - entity.rotate, 2)
|
||||||
|
|
||||||
-- firing arrows
|
-- firing arrows
|
||||||
if ctrl.LMB and ctrl.sneak and shoots then
|
if ctrl.LMB and ctrl.sneak and shoots then
|
||||||
|
|
||||||
local pos = entity.object:get_pos()
|
local pos = entity.object:get_pos()
|
||||||
local obj = minetest.add_entity({
|
local obj = minetest.add_entity(vector.offset(pos, dir.x * 2.5, 1.5 + dir.y, dir.z * 2.5), arrow)
|
||||||
x = pos.x + 0 + dir.x * 2.5,
|
|
||||||
y = pos.y + 1.5 + dir.y,
|
|
||||||
z = pos.z + 0 + dir.z * 2.5}, arrow)
|
|
||||||
|
|
||||||
local ent = obj:get_luaentity()
|
local ent = obj:get_luaentity()
|
||||||
if ent then
|
if ent then
|
||||||
ent.switch = 1 -- for mob specific arrows
|
ent.switch = 1 -- for mob specific arrows
|
||||||
ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
|
ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
|
||||||
local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
|
local vec = vector.new(dir.x * 6, dir.y * 6, dir.z * 6)
|
||||||
local yaw = entity.driver:get_look_horizontal()
|
local yaw = entity.driver:get_look_horizontal()
|
||||||
obj:set_yaw(yaw + math.pi / 2)
|
obj:set_yaw(yaw)
|
||||||
obj:set_velocity(vec)
|
obj:set_velocity(vec)
|
||||||
else
|
else
|
||||||
obj:remove()
|
obj:remove()
|
||||||
@ -439,11 +254,9 @@ function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_an
|
|||||||
|
|
||||||
-- change animation if stopped
|
-- change animation if stopped
|
||||||
if velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
if velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||||
|
entity:set_animation(stand_anim)
|
||||||
mcl_mobs:set_animation(entity, stand_anim)
|
|
||||||
else
|
else
|
||||||
-- moving animation
|
entity:set_animation(moving_anim)
|
||||||
mcl_mobs:set_animation(entity, moving_anim)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -452,12 +265,7 @@ mcl_mobs.mob_class.fly = mcl_mobs.fly
|
|||||||
mcl_mobs.mob_class.attach = mcl_mobs.attach
|
mcl_mobs.mob_class.attach = mcl_mobs.attach
|
||||||
|
|
||||||
function mob_class:on_detach_child(child)
|
function mob_class:on_detach_child(child)
|
||||||
if self.detach_child then
|
if self.detach_child and self.detach_child(self, child) then return end
|
||||||
if self.detach_child(self, child) then
|
if self.driver == child then self.driver = nil end
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if self.driver == child then
|
|
||||||
self.driver = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,14 @@
|
|||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
local mob_class = mcl_mobs.mob_class
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
local PATHFINDING_FAIL_THRESHOLD = 100 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door
|
local PATHFINDING_FAIL_THRESHOLD = 200 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door
|
||||||
local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again
|
local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again
|
||||||
local PATHING_START_DELAY = 4 -- When doing non-prioritised pathing, how long to wait until last mob pathed
|
local PATHING_START_DELAY = 4 -- When doing non-prioritised pathing, how long to wait until last mob pathed
|
||||||
|
|
||||||
local PATHFINDING_SEARCH_DISTANCE = 50 -- How big the square is that pathfinding will look
|
local PATHFINDING_SEARCH_DISTANCE = 25 -- How big the square is that pathfinding will look
|
||||||
|
|
||||||
local PATHFINDING = "gowp"
|
local PATHFINDING = "gowp"
|
||||||
|
|
||||||
local one_down = vector.new(0,-1,0)
|
|
||||||
local one_up = vector.new(0,1,0)
|
|
||||||
|
|
||||||
local plane_adjacents = {
|
local plane_adjacents = {
|
||||||
vector.new(1,0,0),
|
vector.new(1,0,0),
|
||||||
vector.new(-1,0,0),
|
vector.new(-1,0,0),
|
||||||
@ -20,6 +17,7 @@ local plane_adjacents = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false)
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false)
|
||||||
|
local visualize = minetest.settings:get_bool("mcl_mobs_pathfinding_visualize",false)
|
||||||
|
|
||||||
local LOG_MODULE = "[Mobs Pathfinding]"
|
local LOG_MODULE = "[Mobs Pathfinding]"
|
||||||
local function mcl_log (message)
|
local function mcl_log (message)
|
||||||
@ -42,8 +40,8 @@ function append_paths (wp1, wp2)
|
|||||||
mcl_log("Cannot append wp's")
|
mcl_log("Cannot append wp's")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
output_table(wp1)
|
--output_table(wp1)
|
||||||
output_table(wp2)
|
--output_table(wp2)
|
||||||
for _,a in pairs (wp2) do
|
for _,a in pairs (wp2) do
|
||||||
table.insert(wp1, a)
|
table.insert(wp1, a)
|
||||||
end
|
end
|
||||||
@ -51,18 +49,13 @@ function append_paths (wp1, wp2)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function output_enriched (wp_out)
|
local function output_enriched (wp_out)
|
||||||
mcl_log("Output enriched path")
|
--mcl_log("Output enriched path")
|
||||||
local i = 0
|
local i = 0
|
||||||
for _,outy in pairs (wp_out) do
|
for _,outy in pairs (wp_out) do
|
||||||
i = i + 1
|
i = i + 1
|
||||||
mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
|
||||||
|
|
||||||
local action = outy["action"]
|
local action = outy["action"]
|
||||||
if action then
|
if action then
|
||||||
--mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])..", type: " .. action["type"]..", action: " .. action["action"]..", target: " .. minetest.pos_to_string(action["target"]))
|
||||||
mcl_log("type: " .. action["type"])
|
|
||||||
mcl_log("action: " .. action["action"])
|
|
||||||
mcl_log("target: " .. minetest.pos_to_string(action["target"]))
|
|
||||||
end
|
end
|
||||||
--mcl_log("failed attempts: " .. outy["failed_attempts"])
|
--mcl_log("failed attempts: " .. outy["failed_attempts"])
|
||||||
end
|
end
|
||||||
@ -73,33 +66,22 @@ end
|
|||||||
-- an action, such as to open or close a door where we know that pos requires that action
|
-- an action, such as to open or close a door where we know that pos requires that action
|
||||||
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
|
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
|
||||||
local wp_out = {}
|
local wp_out = {}
|
||||||
|
|
||||||
-- TODO Just pass in door position and the index before is open, the index after is close
|
|
||||||
local current_door_index = -1
|
|
||||||
|
|
||||||
for i, cur_pos in pairs(wp_in) do
|
for i, cur_pos in pairs(wp_in) do
|
||||||
local action = nil
|
local action = nil
|
||||||
|
|
||||||
local cur_pos_to_add = vector.add(cur_pos, one_down)
|
if door_open_pos and vector.equals(cur_pos, door_open_pos) then
|
||||||
if door_open_pos and vector.equals (cur_pos, door_open_pos) then
|
|
||||||
mcl_log ("Door open match")
|
mcl_log ("Door open match")
|
||||||
action = {type = "door", action = "open", target = cur_door_pos}
|
action = {type = "door", action = "open", target = cur_door_pos}
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
|
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
|
||||||
mcl_log ("Door close match")
|
mcl_log ("Door close match")
|
||||||
action = {type = "door", action = "close", target = cur_door_pos}
|
action = {type = "door", action = "close", target = cur_door_pos}
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
|
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
|
||||||
mcl_log("Current door pos")
|
mcl_log("Current door pos")
|
||||||
action = {type = "door", action = "open", target = cur_door_pos}
|
action = {type = "door", action = "open", target = cur_door_pos}
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
else
|
|
||||||
cur_pos_to_add = cur_pos
|
|
||||||
--mcl_log ("Pos doesn't match")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
wp_out[i] = {}
|
wp_out[i] = {}
|
||||||
wp_out[i]["pos"] = cur_pos_to_add
|
wp_out[i]["pos"] = cur_pos
|
||||||
wp_out[i]["failed_attempts"] = 0
|
wp_out[i]["failed_attempts"] = 0
|
||||||
wp_out[i]["action"] = action
|
wp_out[i]["action"] = action
|
||||||
|
|
||||||
@ -113,49 +95,39 @@ end
|
|||||||
local last_pathing_time = os.time()
|
local last_pathing_time = os.time()
|
||||||
|
|
||||||
function mob_class:ready_to_path(prioritised)
|
function mob_class:ready_to_path(prioritised)
|
||||||
mcl_log("Check ready to path")
|
-- mcl_log("Check ready to path")
|
||||||
if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then
|
if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then
|
||||||
mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
-- mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
local time_since_path_start = os.time() - last_pathing_time
|
local time_since_path_start = os.time() - last_pathing_time
|
||||||
mcl_log("time_since_path_start: " .. tostring(time_since_path_start))
|
|
||||||
if prioritised or (time_since_path_start) > PATHING_START_DELAY then
|
if prioritised or (time_since_path_start) > PATHING_START_DELAY then
|
||||||
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
|
mcl_log("We are ready to pathfind, no previous fail or we are past threshold: "..tostring(time_since_path_start))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
mcl_log("time_since_path_start: " .. tostring(time_since_path_start))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
|
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
|
||||||
local function calculate_path_through_door (p, cur_door_pos, t)
|
local function calculate_path_through_door (p, cur_door_pos, t)
|
||||||
|
if not cur_door_pos then return end
|
||||||
if t then
|
if t then
|
||||||
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
|
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. " through " .. minetest.pos_to_string(cur_door_pos) .. ", to target: " .. minetest.pos_to_string(t))
|
||||||
else
|
else
|
||||||
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p))
|
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. " through " .. minetest.pos_to_string(cur_door_pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
local enriched_path = nil
|
|
||||||
local wp, prospective_wp
|
|
||||||
|
|
||||||
local pos_closest_to_door = nil
|
|
||||||
local other_side_of_door = nil
|
|
||||||
|
|
||||||
if cur_door_pos then
|
|
||||||
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
|
|
||||||
|
|
||||||
for _,v in pairs(plane_adjacents) do
|
for _,v in pairs(plane_adjacents) do
|
||||||
pos_closest_to_door = vector.add(cur_door_pos,v)
|
local pos_closest_to_door = vector.add(cur_door_pos,v)
|
||||||
other_side_of_door = vector.add(cur_door_pos,-v)
|
|
||||||
|
|
||||||
local n = minetest.get_node(pos_closest_to_door)
|
local n = minetest.get_node(pos_closest_to_door)
|
||||||
|
if not n.walkable then
|
||||||
|
mcl_log("We have open space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
|
||||||
|
|
||||||
if n.name == "air" then
|
local prospective_wp = minetest.find_path(p, pos_closest_to_door, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||||
mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
|
|
||||||
|
|
||||||
prospective_wp = minetest.find_path(p, pos_closest_to_door, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
|
||||||
|
|
||||||
if prospective_wp then
|
if prospective_wp then
|
||||||
|
local other_side_of_door = vector.add(cur_door_pos,-v)
|
||||||
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
||||||
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
||||||
|
|
||||||
@ -167,39 +139,38 @@ local function calculate_path_through_door (p, cur_door_pos, t)
|
|||||||
|
|
||||||
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
||||||
append_paths (prospective_wp, wp_otherside_door_to_target)
|
append_paths (prospective_wp, wp_otherside_door_to_target)
|
||||||
|
|
||||||
wp = prospective_wp
|
|
||||||
mcl_log("We have a path from outside door to target")
|
mcl_log("We have a path from outside door to target")
|
||||||
|
return generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||||
else
|
else
|
||||||
mcl_log("We cannot path from outside door to target")
|
mcl_log("We cannot path from outside door to target")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
mcl_log("No t, just add other side of door")
|
mcl_log("No t, just add other side of door")
|
||||||
table.insert(prospective_wp, other_side_of_door)
|
table.insert(prospective_wp, other_side_of_door)
|
||||||
wp = prospective_wp
|
return generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||||
end
|
|
||||||
|
|
||||||
if wp then
|
|
||||||
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
mcl_log("Cannot path to this air block next to door.")
|
mcl_log("Cannot path to this air block next to door.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
|
||||||
mcl_log("No door found")
|
|
||||||
end
|
|
||||||
|
|
||||||
if wp and not enriched_path then
|
|
||||||
mcl_log("Wp but not enriched")
|
|
||||||
enriched_path = generate_enriched_path(wp)
|
|
||||||
end
|
|
||||||
return enriched_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- we treat ignore as solid, as we cannot path there
|
||||||
|
local function is_solid(pos)
|
||||||
|
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||||
|
return (not ndef) or ndef.walkable
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_open_node(pos, radius)
|
||||||
|
local r = vector.round(pos)
|
||||||
|
if not is_solid(r) then return r end
|
||||||
|
local above = vector.offset(r, 0, 1, 0)
|
||||||
|
if not is_solid(above) then return above, true end -- additional return: drop last
|
||||||
|
local n = minetest.find_node_near(pos, radius or 1, {"air"})
|
||||||
|
if n then return n end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function mob_class:gopath(target, callback_arrived, prioritised)
|
function mob_class:gopath(target, callback_arrived, prioritised)
|
||||||
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
||||||
@ -209,8 +180,19 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||||||
|
|
||||||
self.order = nil
|
self.order = nil
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
-- maybe feet are buried in solid?
|
||||||
local t = vector.offset(target,0,1,0)
|
local start = self.object:get_pos()
|
||||||
|
local p = find_open_node(start, 1)
|
||||||
|
if not p then -- buried?
|
||||||
|
minetest.log("action", "Cannot path from "..minetest.pos_to_string(start).." because it is solid. Nodetype: "..minetest.get_node(start).name)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- target might be a job-site that is solid
|
||||||
|
local t, drop_last_wp = find_open_node(target, 1)
|
||||||
|
if not t then
|
||||||
|
minetest.log("action", "Cannot path to "..minetest.pos_to_string(target).." because it is solid. Nodetype: "..minetest.get_node(target).name)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
--Check direct route
|
--Check direct route
|
||||||
local wp = minetest.find_path(p, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
local wp = minetest.find_path(p, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||||
@ -218,11 +200,15 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||||||
if not wp then
|
if not wp then
|
||||||
mcl_log("### No direct path. Path through door closest to target.")
|
mcl_log("### No direct path. Path through door closest to target.")
|
||||||
local door_near_target = minetest.find_node_near(target, 16, {"group:door"})
|
local door_near_target = minetest.find_node_near(target, 16, {"group:door"})
|
||||||
|
local below = door_near_target and vector.offset(door_near_target, 0, -1, 0)
|
||||||
|
if below and minetest.get_item_group(minetest.get_node(below), "door") > 0 then door_near_target = below end
|
||||||
wp = calculate_path_through_door(p, door_near_target, t)
|
wp = calculate_path_through_door(p, door_near_target, t)
|
||||||
|
|
||||||
if not wp then
|
if not wp then
|
||||||
mcl_log("### No path though door closest to target. Try door closest to origin.")
|
mcl_log("### No path though door closest to target. Try door closest to origin.")
|
||||||
local door_closest = minetest.find_node_near(p, 16, {"group:door"})
|
local door_closest = minetest.find_node_near(p, 16, {"group:door"})
|
||||||
|
local below = door_closest and vector.offset(door_closest, 0, -1, 0)
|
||||||
|
if below and minetest.get_item_group(minetest.get_node(below), "door") > 0 then door_closest = below end
|
||||||
wp = calculate_path_through_door(p, door_closest, t)
|
wp = calculate_path_through_door(p, door_closest, t)
|
||||||
|
|
||||||
-- Path through 2 doors
|
-- Path through 2 doors
|
||||||
@ -236,7 +222,7 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||||||
|
|
||||||
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
|
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
|
||||||
if pos_after_door_entry then
|
if pos_after_door_entry then
|
||||||
local pos_after_door = vector.add(pos_after_door_entry["pos"], one_up)
|
local pos_after_door = pos_after_door_entry["pos"]
|
||||||
mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door))
|
mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door))
|
||||||
local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t)
|
local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t)
|
||||||
if path_after_door and #path_after_door > 1 then
|
if path_after_door and #path_after_door > 1 then
|
||||||
@ -268,26 +254,92 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||||||
-- If cannot path, don't immediately try again
|
-- If cannot path, don't immediately try again
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- todo: we would also need to avoid overhangs, but minetest.find_path cannot help us there
|
||||||
|
-- we really need a better pathfinder overall.
|
||||||
|
|
||||||
|
-- try to find a way around fences and walls. This is very barebones, but at least it should
|
||||||
|
-- help path around very simple fences *IF* there is a detour that does not require jumping or gates.
|
||||||
if wp and #wp > 0 then
|
if wp and #wp > 0 then
|
||||||
|
local i = 1
|
||||||
|
while i < #wp do
|
||||||
|
-- fence or wall underneath?
|
||||||
|
local bdef = minetest.registered_nodes[minetest.get_node(vector.offset(wp[i].pos, 0, -1, 0)).name]
|
||||||
|
if not bdef then minetest.log("warning", "There must not be unknown nodes on path") end
|
||||||
|
-- carpets are fine
|
||||||
|
if bdef and (bdef.groups.carpet or 0) > 0 then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
-- target bottom of door
|
||||||
|
elseif bdef and (bdef.groups.door or 0) > 0 then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
-- not walkable?
|
||||||
|
elseif bdef and not bdef.walkable then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
i = i - 1
|
||||||
|
-- plan opening fence gates
|
||||||
|
elseif bdef and (bdef.groups.fence_gate or 0) > 0 then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
wp[math.max(1,i-1)].action = {type = "door", action = "open", target = wp[i].pos}
|
||||||
|
if i+1 < #wp then
|
||||||
|
wp[i+1].action = {type = "door", action = "close", target = wp[i].pos}
|
||||||
|
end
|
||||||
|
-- do not jump on fences and walls, but try to walk around
|
||||||
|
elseif bdef and i > 1 and ((bdef.groups.fence or 0) > 0 or (bdef.groups.wall or 0) > 0) and wp[i].pos.y > wp[i-1].pos.y then
|
||||||
|
-- find end of wall(s)
|
||||||
|
local j = i + 1
|
||||||
|
while j <= #wp do
|
||||||
|
local below = vector.offset(wp[j].pos, 0, -1, 0)
|
||||||
|
local bdef = minetest.registered_nodes[minetest.get_node(below).name]
|
||||||
|
if not bdef or ((bdef.groups.fence or 0) == 0 and (bdef.groups.wall or 0) == 0) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
-- minetest.log("warning", bdef.name .. " at "..tostring(i).." end at "..(j <= #wp and tostring(j) or "nil"))
|
||||||
|
if j <= #wp and wp[i-1].pos.y == wp[j].pos.y then
|
||||||
|
local swp = minetest.find_path(wp[i-1].pos, wp[j].pos, PATHFINDING_SEARCH_DISTANCE, 0, 0)
|
||||||
|
-- TODO: if we do not find a path here, consider pathing through a fence gate!
|
||||||
|
if swp and #swp > 0 then
|
||||||
|
for k = j-1,i,-1 do table.remove(wp, k) end
|
||||||
|
for k = 2, #swp-1 do table.insert(wp, i-2+k, {pos = swp[k], failed_attempts = 0}) end
|
||||||
|
--minetest.log("warning", "Monkey patch pathfinding around "..bdef.name.." successful.")
|
||||||
|
i = i + #swp - 4
|
||||||
|
else
|
||||||
|
--minetest.log("warning", "Monkey patch pathfinding around "..bdef.name.." failed.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if wp and drop_last_wp and vector.equals(wp[#wp], t) then table.remove(wp, #wp) end
|
||||||
|
if wp and #wp > 0 then
|
||||||
|
if visualize then
|
||||||
|
for i = 1,#wp do
|
||||||
|
core.add_particle({pos = wp[i].pos, expirationtime=3+i/3, size=3+2/i, velocity=vector.new(0,-0.02,0),
|
||||||
|
texture="mcl_copper_anti_oxidation_particle.png"}) -- white stars
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--output_table(wp)
|
--output_table(wp)
|
||||||
self._target = t
|
self._target = t
|
||||||
self.callback_arrived = callback_arrived
|
self.callback_arrived = callback_arrived
|
||||||
local current_location = table.remove(wp,1)
|
self.current_target = table.remove(wp,1)
|
||||||
if current_location and current_location["pos"] then
|
while self.current_target and self.current_target.pos and vector.distance(p, self.current_target.pos) < 0.5 do
|
||||||
mcl_log("Removing first co-ord? " .. tostring(current_location["pos"]))
|
--mcl_log("Skipping close initial waypoint")
|
||||||
else
|
self.current_target = table.remove(wp,1)
|
||||||
mcl_log("Nil pos")
|
|
||||||
end
|
end
|
||||||
self.current_target = current_location
|
if self.current_target and self.current_target.pos then
|
||||||
|
self:turn_in_direction(self.current_target.pos.x - p.x, self.current_target.pos.z - p.z, 2)
|
||||||
self.waypoints = wp
|
self.waypoints = wp
|
||||||
self.state = PATHFINDING
|
self.state = PATHFINDING
|
||||||
return true
|
return true
|
||||||
else
|
end
|
||||||
|
end
|
||||||
|
self:turn_in_direction(target.x - p.x, target.z - p.z, 4)
|
||||||
self.state = "walk"
|
self.state = "walk"
|
||||||
self.waypoints = nil
|
self.waypoints = nil
|
||||||
self.current_target = nil
|
self.current_target = nil
|
||||||
-- minetest.log("no path found")
|
--minetest.log("no path found")
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:interact_with_door(action, target)
|
function mob_class:interact_with_door(action, target)
|
||||||
@ -300,19 +352,27 @@ function mob_class:interact_with_door(action, target)
|
|||||||
|
|
||||||
local n = minetest.get_node(target)
|
local n = minetest.get_node(target)
|
||||||
if n.name:find("_b_") or n.name:find("_t_") then
|
if n.name:find("_b_") or n.name:find("_t_") then
|
||||||
mcl_log("Door")
|
|
||||||
local def = minetest.registered_nodes[n.name]
|
local def = minetest.registered_nodes[n.name]
|
||||||
local closed = n.name:find("_b_1") or n.name:find("_t_1")
|
local meta = minetest.get_meta(target)
|
||||||
--if self.state == PATHFINDING then
|
local closed = meta:get_int("is_open") == 0
|
||||||
if closed and action == "open" and def.on_rightclick then
|
if closed and action == "open" and def.on_rightclick then
|
||||||
mcl_log("Open door")
|
mcl_log("Open door")
|
||||||
def.on_rightclick(target,n,self)
|
def.on_rightclick(target,n,self)
|
||||||
end
|
elseif not closed and action == "close" and def.on_rightclick then
|
||||||
if not closed and action == "close" and def.on_rightclick then
|
|
||||||
mcl_log("Close door")
|
mcl_log("Close door")
|
||||||
def.on_rightclick(target,n,self)
|
def.on_rightclick(target,n,self)
|
||||||
end
|
end
|
||||||
--else
|
elseif n.name:find("_gate") then
|
||||||
|
local def = minetest.registered_nodes[n.name]
|
||||||
|
local meta = minetest.get_meta(target)
|
||||||
|
local closed = meta:get_int("state") == 0
|
||||||
|
if closed and action == "open" and def.on_rightclick then
|
||||||
|
mcl_log("Open gate")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
elseif not closed and action == "close" and def.on_rightclick then
|
||||||
|
mcl_log("Close gate")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
mcl_log("Not door")
|
mcl_log("Not door")
|
||||||
end
|
end
|
||||||
@ -333,6 +393,7 @@ function mob_class:do_pathfind_action(action)
|
|||||||
end
|
end
|
||||||
if type and type == "door" then
|
if type and type == "door" then
|
||||||
mcl_log("Type is door")
|
mcl_log("Type is door")
|
||||||
|
self.object:set_velocity(vector.zero())
|
||||||
self:interact_with_door(action_val, target)
|
self:interact_with_door(action_val, target)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -343,8 +404,7 @@ function mob_class:check_gowp(dtime)
|
|||||||
|
|
||||||
-- no destination
|
-- no destination
|
||||||
if not p or not self._target then
|
if not p or not self._target then
|
||||||
mcl_log("p: ".. tostring(p))
|
mcl_log("p: ".. tostring(p)..", self._target: ".. tostring(self._target))
|
||||||
mcl_log("self._target: ".. tostring(self._target))
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -358,8 +418,8 @@ function mob_class:check_gowp(dtime)
|
|||||||
self.current_target = nil
|
self.current_target = nil
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
self.order = "stand"
|
self.order = "stand"
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
self.object:set_velocity(vector.zero())
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration(vector.zero())
|
||||||
if self.callback_arrived then return self.callback_arrived(self) end
|
if self.callback_arrived then return self.callback_arrived(self) end
|
||||||
return true
|
return true
|
||||||
elseif not self.current_target then
|
elseif not self.current_target then
|
||||||
@ -368,41 +428,67 @@ function mob_class:check_gowp(dtime)
|
|||||||
|
|
||||||
-- More pathing to be done
|
-- More pathing to be done
|
||||||
local distance_to_current_target = 50
|
local distance_to_current_target = 50
|
||||||
if self.current_target and self.current_target["pos"] then
|
if self.current_target and self.current_target.pos then
|
||||||
distance_to_current_target = vector.distance(p,self.current_target["pos"])
|
local dx, dy, dz = self.current_target.pos.x-p.x, self.current_target.pos.y-p.y, self.current_target.pos.z-p.z
|
||||||
|
distance_to_current_target = (dx*dx+dy*dy*0.5+dz*dz)^0.5 -- reduced weight on y
|
||||||
|
--distance_to_current_target = vector.distance(p,self.current_target.pos)
|
||||||
|
end
|
||||||
|
-- also check next target, maybe we were too fast
|
||||||
|
local next_target = #self.waypoints > 1 and self.waypoints[1]
|
||||||
|
if not self.current_target["action"] and next_target and next_target.pos and distance_to_current_target < 1.5 then
|
||||||
|
local dx, dy, dz = next_target.pos.x-p.x, next_target.pos.y-p.y, next_target.pos.z-p.z
|
||||||
|
local distance_to_next_target = (dx*dx+dy*dy*0.5+dz*dz)^0.5 -- reduced weight on y
|
||||||
|
if distance_to_next_target < distance_to_current_target then
|
||||||
|
mcl_log("Skipped one waypoint.")
|
||||||
|
self.current_target = table.remove(self.waypoints, 1) -- pop waypoint already
|
||||||
|
distance_to_current_target = distance_to_next_target
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- debugging tool
|
||||||
|
if visualize and self.current_target and self.current_target.pos then
|
||||||
|
core.add_particle({pos = self.current_target.pos, expirationtime=.1, size=3, velocity=vector.new(0,-0.2,0), texture="mcl_particles_flame.png"})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 0.6 is working but too sensitive. sends villager back too frequently. 0.7 is quite good, but not with heights
|
-- 0.6 is working but too sensitive. sends villager back too frequently. 0.7 is quite good, but not with heights
|
||||||
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
|
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
|
||||||
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
|
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
|
||||||
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < 0.9 ) then
|
local threshold = self.current_target["action"] and 0.7 or 0.9
|
||||||
|
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target.pos or distance_to_current_target < threshold ) then
|
||||||
-- We have waypoints, and are at current_target or have no current target. We need a new current_target.
|
-- We have waypoints, and are at current_target or have no current target. We need a new current_target.
|
||||||
self:do_pathfind_action (self.current_target["action"])
|
self:do_pathfind_action (self.current_target["action"])
|
||||||
|
|
||||||
local failed_attempts = self.current_target["failed_attempts"]
|
local failed_attempts = self.current_target["failed_attempts"]
|
||||||
mcl_log("There after " .. failed_attempts .. " failed attempts. current target:".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: " .. distance_to_current_target)
|
mcl_log("There after " .. failed_attempts .. " failed attempts. current target:".. minetest.pos_to_string(self.current_target.pos) .. ". Distance: " .. distance_to_current_target)
|
||||||
|
|
||||||
|
local hurry = (self.order == "sleep" or #self.waypoints > 15) and self.run_velocity or self.walk_velocity
|
||||||
self.current_target = table.remove(self.waypoints, 1)
|
self.current_target = table.remove(self.waypoints, 1)
|
||||||
self:go_to_pos(self.current_target["pos"])
|
-- use smoothing -- TODO: check for blockers before cutting corners?
|
||||||
|
if #self.waypoints > 0 and not self.current_target["action"] then
|
||||||
|
local curwp, nextwp = self.current_target.pos, self.waypoints[1].pos
|
||||||
|
self:go_to_pos(vector.new(curwp.x*0.7+nextwp.x*0.3,curwp.y,curwp.z*0.7+nextwp.z*0.3), hurry)
|
||||||
return
|
return
|
||||||
elseif self.current_target and self.current_target["pos"] then
|
end
|
||||||
|
self:go_to_pos(self.current_target.pos, hurry)
|
||||||
|
--if self.current_target["action"] then self:set_velocity(self.walk_velocity * 0.5) end
|
||||||
|
return
|
||||||
|
elseif self.current_target and self.current_target.pos then
|
||||||
-- No waypoints left, but have current target and not close enough. Potentially last waypoint to go to.
|
-- No waypoints left, but have current target and not close enough. Potentially last waypoint to go to.
|
||||||
|
|
||||||
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
||||||
local failed_attempts = self.current_target["failed_attempts"]
|
local failed_attempts = self.current_target["failed_attempts"]
|
||||||
if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then
|
if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then
|
||||||
mcl_log("Failed to reach position (" .. minetest.pos_to_string(self.current_target["pos"]) .. ") too many times. Abandon route. Times tried: " .. failed_attempts)
|
mcl_log("Failed to reach position " .. minetest.pos_to_string(self.current_target.pos) .. " too many times. At: "..minetest.pos_to_string(p).." Abandon route. Times tried: " .. failed_attempts .. " current distance "..distance_to_current_target)
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
self.current_target = nil
|
self.current_target = nil
|
||||||
self.waypoints = nil
|
self.waypoints = nil
|
||||||
self._target = nil
|
self._target = nil
|
||||||
self._pf_last_failed = os.time()
|
self._pf_last_failed = os.time()
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
self.object:set_velocity(vector.zero())
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration(vector.zero())
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
--mcl_log("Not at pos with failed attempts ".. failed_attempts ..": ".. minetest.pos_to_string(p) .. "self.current_target: ".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: ".. distance_to_current_target)
|
--mcl_log("Not at pos with failed attempts ".. failed_attempts ..": ".. minetest.pos_to_string(p) .. "self.current_target: ".. minetest.pos_to_string(self.current_target.pos) .. ". Distance: ".. distance_to_current_target)
|
||||||
self:go_to_pos(self.current_target["pos"])
|
self:go_to_pos(self.current_target["pos"])
|
||||||
-- Do i just delete current_target, and return so we can find final path.
|
-- Do i just delete current_target, and return so we can find final path.
|
||||||
else
|
else
|
||||||
@ -436,6 +522,7 @@ function mob_class:check_gowp(dtime)
|
|||||||
|
|
||||||
-- I don't think we need the following anymore, but test first.
|
-- I don't think we need the following anymore, but test first.
|
||||||
-- Maybe just need something to path to target if no waypoints left
|
-- Maybe just need something to path to target if no waypoints left
|
||||||
|
--[[ ok, let's try
|
||||||
if self.current_target and self.current_target["pos"] and (self.waypoints and #self.waypoints == 0) then
|
if self.current_target and self.current_target["pos"] and (self.waypoints and #self.waypoints == 0) then
|
||||||
local updated_p = self.object:get_pos()
|
local updated_p = self.object:get_pos()
|
||||||
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
||||||
@ -444,7 +531,7 @@ function mob_class:check_gowp(dtime)
|
|||||||
mcl_log("Current p: ".. minetest.pos_to_string(updated_p))
|
mcl_log("Current p: ".. minetest.pos_to_string(updated_p))
|
||||||
|
|
||||||
-- 1.6 is good. is 1.9 better? It could fail less, but will it path to door when it isn't after door
|
-- 1.6 is good. is 1.9 better? It could fail less, but will it path to door when it isn't after door
|
||||||
if distance_to_cur_targ > 1.9 then
|
if distance_to_cur_targ > 1.6 then
|
||||||
mcl_log("not close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
mcl_log("not close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
||||||
self:go_to_pos(self._current_target)
|
self:go_to_pos(self._current_target)
|
||||||
else
|
else
|
||||||
@ -454,4 +541,5 @@ function mob_class:check_gowp(dtime)
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
end
|
end
|
||||||
|
@ -6,6 +6,18 @@ local ENTITY_CRAMMING_MAX = 24
|
|||||||
local CRAMMING_DAMAGE = 3
|
local CRAMMING_DAMAGE = 3
|
||||||
local DEATH_DELAY = 0.5
|
local DEATH_DELAY = 0.5
|
||||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||||
|
local PI = math.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 PATHFINDING = "gowp"
|
||||||
local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
|
local mobs_debug = minetest.settings:get_bool("mobs_debug", false)
|
||||||
@ -13,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 mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48
|
||||||
local show_health = false
|
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)
|
-- check if within physical map limits (-30911 to 30927)
|
||||||
local function within_limits(pos, radius)
|
local function within_limits(pos, radius)
|
||||||
local wmin, wmax = -30912, 30928
|
local wmin, wmax = -30912, 30928
|
||||||
@ -56,9 +54,7 @@ end
|
|||||||
|
|
||||||
-- Return true if object is in view_range
|
-- Return true if object is in view_range
|
||||||
function mob_class:object_in_range(object)
|
function mob_class:object_in_range(object)
|
||||||
if not object then
|
if not object then return false end
|
||||||
return false
|
|
||||||
end
|
|
||||||
local factor
|
local factor
|
||||||
-- Apply view range reduction for special player armor
|
-- Apply view range reduction for special player armor
|
||||||
if object:is_player() then
|
if object:is_player() then
|
||||||
@ -110,24 +106,21 @@ function mob_class:item_drop(cooked, looting_level)
|
|||||||
|
|
||||||
local num = 0
|
local num = 0
|
||||||
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
local do_common_looting = (looting_level > 0 and looting_type == "common")
|
||||||
if math.random() < chance then
|
if random() < chance then
|
||||||
num = math.random(dropdef.min or 1, dropdef.max or 1)
|
num = random(dropdef.min or 1, dropdef.max or 1)
|
||||||
elseif not dropdef.looting_ignore_chance then
|
elseif not dropdef.looting_ignore_chance then
|
||||||
do_common_looting = false
|
do_common_looting = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if do_common_looting then
|
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
|
end
|
||||||
|
|
||||||
if num > 0 then
|
if num > 0 then
|
||||||
item = dropdef.name
|
item = dropdef.name
|
||||||
|
|
||||||
if cooked then
|
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
|
if output and output.item and not output.item:is_empty() then
|
||||||
item = output.item:get_name()
|
item = output.item:get_name()
|
||||||
end
|
end
|
||||||
@ -135,20 +128,15 @@ function mob_class:item_drop(cooked, looting_level)
|
|||||||
|
|
||||||
for x = 1, num do
|
for x = 1, num do
|
||||||
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
obj = minetest.add_item(pos, ItemStack(item .. " " .. 1))
|
||||||
end
|
|
||||||
|
|
||||||
if obj and obj:get_luaentity() then
|
if obj and obj:get_luaentity() then
|
||||||
|
obj:set_velocity(vector.new((random() - 0.5) * 1.5, 6, (random() - 0.5) * 1.5))
|
||||||
obj:set_velocity({
|
|
||||||
x = math.random(-10, 10) / 9,
|
|
||||||
y = 6,
|
|
||||||
z = math.random(-10, 10) / 9,
|
|
||||||
})
|
|
||||||
elseif obj then
|
elseif obj then
|
||||||
obj:remove() -- item does not exist
|
obj:remove() -- item does not exist
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
self.drops = {}
|
self.drops = {}
|
||||||
end
|
end
|
||||||
@ -156,32 +144,29 @@ end
|
|||||||
-- collision function borrowed amended from jordan4ibanez open_ai mod
|
-- collision function borrowed amended from jordan4ibanez open_ai mod
|
||||||
function mob_class:collision()
|
function mob_class:collision()
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not pos then return {0,0} end
|
if not pos then return 0,0 end
|
||||||
local vel = self.object:get_velocity()
|
local vel = self.object:get_velocity()
|
||||||
local x = 0
|
local x, z = 0, 0
|
||||||
local z = 0
|
|
||||||
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
||||||
|
|
||||||
local ent = object:get_luaentity()
|
local ent = object:get_luaentity()
|
||||||
if object:is_player() or (ent and ent.is_mob and object ~= self.object) then
|
if object:is_player() or (ent and ent.is_mob and object ~= self.object) then
|
||||||
|
|
||||||
if object:is_player() and mcl_burning.is_burning(self.object) then
|
if object:is_player() and mcl_burning.is_burning(self.object) then
|
||||||
mcl_burning.set_on_fire(object, 4)
|
mcl_burning.set_on_fire(object, 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
local pos2 = object:get_pos()
|
local pos2 = object:get_pos()
|
||||||
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
|
local vx, vz = pos.x - pos2.x, pos.z - pos2.z
|
||||||
local force = (width + 0.5) - vector.distance(
|
local force = width - (vx*vx+vz*vz)^0.5
|
||||||
{x = pos.x, y = 0, z = pos.z},
|
if force > 0 then
|
||||||
{x = pos2.x, y = 0, z = pos2.z})
|
force = force * force * (object:is_player() and 2 or 1) -- players push more
|
||||||
|
-- minetest.log("mob push force "..force.." "..tostring(self.name).." by "..tostring(ent and ent.name or "player"))
|
||||||
x = x + (vec.x * force)
|
x = x + vx * force
|
||||||
z = z + (vec.z * force)
|
z = z + vz * force
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
return({x,z})
|
return x, z
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:check_death_and_slow_mob()
|
function mob_class:check_death_and_slow_mob()
|
||||||
@ -192,196 +177,113 @@ function mob_class:check_death_and_slow_mob()
|
|||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
if v then
|
if v then
|
||||||
--diffuse object velocity
|
--diffuse object velocity
|
||||||
self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d})
|
self.object:set_velocity(vector.new(v.x*d, v.y, v.z*d))
|
||||||
end
|
end
|
||||||
return dying
|
return dying
|
||||||
end
|
end
|
||||||
|
|
||||||
-- move mob in facing direction
|
-- move mob in facing direction
|
||||||
function mob_class:set_velocity(v)
|
function mob_class:set_velocity(v)
|
||||||
if not v then return end
|
local c_x, c_z = 0, 0
|
||||||
|
|
||||||
local c_x, c_y = 0, 0
|
|
||||||
|
|
||||||
-- can mob be pushed, if so calculate direction
|
-- can mob be pushed, if so calculate direction
|
||||||
if self.pushable then
|
if self.pushable then
|
||||||
c_x, c_y = unpack(self:collision())
|
c_x, c_z = self:collision()
|
||||||
end
|
end
|
||||||
|
if v > 0 then
|
||||||
-- halt mob if it has been ordered to stay
|
|
||||||
if self.order == "stand" or self.order == "sit" then
|
|
||||||
self.acc = vector.zero()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local yaw = (self.object:get_yaw() or 0) + self.rotate
|
local yaw = (self.object:get_yaw() or 0) + self.rotate
|
||||||
local vv = self.object:get_velocity()
|
local x = ((-sin(yaw) * v) + c_x) * .4
|
||||||
|
local z = (( cos(yaw) * v) + c_z) * .4
|
||||||
if vv and yaw then
|
if not self.acc then
|
||||||
self.acc = vector.new(((math.sin(yaw) * -v) + c_x) * .4, 0, ((math.cos(yaw) * v) + c_y) * .4)
|
self.acc = vector.new(x, 0, z)
|
||||||
|
else
|
||||||
|
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, self.acc.y, self.acc.z = c_x * .2, 0, c_z * .2
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- calculate mob velocity
|
-- calculate mob velocity (2d)
|
||||||
function mob_class:get_velocity()
|
function mob_class:get_velocity()
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
if v then
|
if not v then return 0 end
|
||||||
return (v.x * v.x + v.z * v.z) ^ 0.5
|
return (v.x*v.x + v.z*v.z)^0.5
|
||||||
end
|
|
||||||
|
|
||||||
return 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:update_roll()
|
function mob_class:update_roll()
|
||||||
local is_Fleckenstein = self.nametag == "Fleckenstein"
|
local is_Fleckenstein = self.nametag == "Fleckenstein"
|
||||||
local was_Fleckenstein = false
|
if not is_Fleckenstein and not self.is_Fleckenstein then return end
|
||||||
|
|
||||||
local rot = self.object:get_rotation()
|
local rot = self.object:get_rotation()
|
||||||
rot.z = is_Fleckenstein and pi or 0
|
rot.z = is_Fleckenstein and PI or 0
|
||||||
self.object:set_rotation(rot)
|
self.object:set_rotation(rot)
|
||||||
|
|
||||||
local cbox = table.copy(self.collisionbox)
|
if is_Fleckenstein ~= self.is_Fleckenstein then
|
||||||
local acbox = self.object:get_properties().collisionbox
|
|
||||||
|
|
||||||
if math.abs(cbox[2] - acbox[2]) > 0.1 then
|
|
||||||
was_Fleckenstein = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_Fleckenstein ~= was_Fleckenstein then
|
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
pos.y = pos.y + (acbox[2] + acbox[5])
|
local cbox = is_Fleckenstein and table.copy(self.collisionbox) or self.object:get_properties().collisionbox
|
||||||
self.object:set_pos(pos)
|
pos.y = pos.y + (cbox[2] + cbox[5])
|
||||||
end
|
|
||||||
|
|
||||||
if is_Fleckenstein then
|
|
||||||
cbox[2], cbox[5] = -cbox[5], -cbox[2]
|
cbox[2], cbox[5] = -cbox[5], -cbox[2]
|
||||||
self.object:set_properties({collisionbox = cbox})
|
|
||||||
-- This leads to child mobs having the wrong collisionbox
|
-- This leads to child mobs having the wrong collisionbox
|
||||||
-- and seeing as it seems to be nothing but an easter egg
|
-- and seeing as it seems to be nothing but an easter egg
|
||||||
-- i've put it inside the if. Which just makes it be upside
|
-- i've put it inside the if. Which just makes it be upside
|
||||||
-- down lol.
|
-- down lol.
|
||||||
|
self.object:set_properties({collisionbox = cbox})
|
||||||
|
self.object:set_pos(pos)
|
||||||
end
|
end
|
||||||
|
self.is_Fleckenstein = is_Fleckenstein
|
||||||
end
|
end
|
||||||
|
|
||||||
local function shortest_term_of_yaw_rotation(self, rot_origin, rot_target, nums)
|
-- Relative turn, primarily for random turning
|
||||||
|
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||||
if not rot_origin or not rot_target then
|
function mob_class:turn_by(angle, delay, dtime)
|
||||||
return
|
return self:set_yaw((self.object:get_yaw() or 0) + angle, delay, dtime)
|
||||||
end
|
end
|
||||||
|
-- Turn into a direction (e.g., to the player, or away)
|
||||||
rot_origin = math.deg(rot_origin)
|
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||||
rot_target = math.deg(rot_target)
|
function mob_class:turn_in_direction(dx, dz, delay, dtime)
|
||||||
|
if abs(dx) == 0 and abs(dz) == 0 then return self.object:get_yaw() + self.rotate end
|
||||||
if rot_origin < rot_target then
|
return self:set_yaw(-atan2(dx, dz) - self.rotate, delay, dtime) + self.rotate
|
||||||
if math.abs(rot_origin-rot_target)<180 then
|
|
||||||
if nums then
|
|
||||||
return rot_target-rot_origin
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if nums then
|
|
||||||
return -(rot_origin-(rot_target-360))
|
|
||||||
else
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if math.abs(rot_origin-rot_target)<180 then
|
|
||||||
if nums then
|
|
||||||
return rot_target-rot_origin
|
|
||||||
else
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if nums then
|
|
||||||
return (rot_target-(rot_origin-360))
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- set and return valid yaw
|
-- set and return valid yaw
|
||||||
|
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||||
function mob_class:set_yaw(yaw, delay, dtime)
|
function mob_class:set_yaw(yaw, delay, dtime)
|
||||||
if self.noyaw then return end
|
if self.noyaw then return end
|
||||||
|
if self._kb_turn then return yaw end -- knockback in effect
|
||||||
if not self.object:get_yaw() or not self.object:get_pos() then return end
|
if not self.object:get_yaw() or not self.object:get_pos() then return end
|
||||||
|
self.delay = delay or 0
|
||||||
if self.state ~= PATHFINDING then
|
self.target_yaw = yaw % TWOPI
|
||||||
self._turn_to = yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
--mcl_log("Yaw is: \t\t" .. tostring(math.deg(yaw)))
|
|
||||||
--mcl_log("self.object:get_yaw() is: \t" .. tostring(math.deg(self.object:get_yaw())))
|
|
||||||
|
|
||||||
--clamp our yaw to a 360 range
|
|
||||||
if math.deg(self.object:get_yaw()) > 360 then
|
|
||||||
self.object:set_yaw(math.rad(0))
|
|
||||||
elseif math.deg(self.object:get_yaw()) < 0 then
|
|
||||||
self.object:set_yaw(math.rad(360))
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.deg(yaw) > 360 then
|
|
||||||
yaw=math.rad(math.deg(yaw)%360)
|
|
||||||
elseif math.deg(yaw) < 0 then
|
|
||||||
yaw=math.rad(((360*5)-math.deg(yaw))%360)
|
|
||||||
end
|
|
||||||
|
|
||||||
--calculate the shortest way to turn to find our target
|
|
||||||
local target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), yaw, false)
|
|
||||||
local target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), yaw, true)
|
|
||||||
|
|
||||||
--turn in the shortest path possible toward our target. if we are attacking, don't dance.
|
|
||||||
if (math.abs(target_shortest_path) > 50 and not self._kb_turn) and (self.attack and self.attack:get_pos() or self.following and self.following:get_pos()) then
|
|
||||||
if self.following then
|
|
||||||
target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), true)
|
|
||||||
target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.following:get_pos())), false)
|
|
||||||
else
|
|
||||||
target_shortest_path = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.attack:get_pos())), true)
|
|
||||||
target_shortest_path_nums = shortest_term_of_yaw_rotation(self, self.object:get_yaw(), minetest.dir_to_yaw(vector.direction(self.object:get_pos(), self.attack:get_pos())), false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ddtime = 0.05 --set_tick_rate
|
|
||||||
|
|
||||||
if dtime then
|
|
||||||
ddtime = dtime
|
|
||||||
end
|
|
||||||
|
|
||||||
if math.abs(target_shortest_path_nums) > 10 then
|
|
||||||
self.object:set_yaw(self.object:get_yaw()+(target_shortest_path*(3.6*ddtime)))
|
|
||||||
if validate_vector(self.acc) then
|
|
||||||
self.acc=vector.rotate_around_axis(self.acc,vector.new(0,1,0), target_shortest_path*(3.6*ddtime))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
delay = delay or 0
|
|
||||||
|
|
||||||
yaw = self.object:get_yaw()
|
|
||||||
|
|
||||||
if delay == 0 then
|
|
||||||
if self.shaking and dtime then
|
|
||||||
yaw = yaw + (math.random() * 2 - 1) * 5 * dtime
|
|
||||||
end
|
|
||||||
--self:update_roll()
|
|
||||||
return yaw
|
|
||||||
end
|
|
||||||
|
|
||||||
self.target_yaw = yaw
|
|
||||||
self.delay = delay
|
|
||||||
|
|
||||||
return self.target_yaw
|
return self.target_yaw
|
||||||
end
|
end
|
||||||
|
|
||||||
-- global function to set mob yaw
|
-- improved smooth rotation
|
||||||
function mcl_mobs.yaw(self, yaw, delay, dtime)
|
function mob_class:check_smooth_rotation(dtime)
|
||||||
return mob_class.set_yaw(self, yaw, delay, dtime)
|
if not self.target_yaw then return end
|
||||||
|
|
||||||
|
local delay = self.delay
|
||||||
|
local initial_yaw = self.object:get_yaw() or 0
|
||||||
|
local yaw -- resulting yaw for this tick
|
||||||
|
if delay and delay > 1 then
|
||||||
|
local dif = (self.target_yaw - initial_yaw + PI) % TWOPI - PI
|
||||||
|
yaw = (initial_yaw + dif / delay) % TWOPI
|
||||||
|
self.delay = delay - 1
|
||||||
|
else
|
||||||
|
yaw = self.target_yaw
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.shaking then
|
||||||
|
yaw = yaw + (random() * 2 - 1) / 72 * dtime
|
||||||
|
end
|
||||||
|
--[[ needed? if self.acc then
|
||||||
|
local change = yaw - initial_yaw
|
||||||
|
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)
|
||||||
|
self:update_roll()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- are we flying in what we are suppose to? (taikedz)
|
-- are we flying in what we are suppose to? (taikedz)
|
||||||
@ -489,7 +391,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
|
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 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
|
if not mcl_sculk.handle_death(pos, xp_amount) then
|
||||||
--minetest.log("Xp not thrown")
|
--minetest.log("Xp not thrown")
|
||||||
@ -562,7 +464,7 @@ function mob_class:check_for_death(cause, cmi_cause)
|
|||||||
elseif self.animation and self.animation.die_start and self.animation.die_end then
|
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 frames = self.animation.die_end - self.animation.die_start
|
||||||
local speed = self.animation.die_speed or 15
|
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")
|
self:set_animation( "die")
|
||||||
else
|
else
|
||||||
length = 1 + DEATH_DELAY
|
length = 1 + DEATH_DELAY
|
||||||
@ -662,7 +564,7 @@ function mob_class:do_env_damage()
|
|||||||
|
|
||||||
-- what is mob standing in?
|
-- what is mob standing in?
|
||||||
pos.y = pos.y + y_level + 0.25 -- foot level
|
pos.y = pos.y + y_level + 0.25 -- foot level
|
||||||
local pos2 = {x=pos.x, y=pos.y-1, z=pos.z}
|
local pos2 = vector.new(pos.x, pos.y-1, pos.z)
|
||||||
self.standing_in = node_ok(pos, "air").name
|
self.standing_in = node_ok(pos, "air").name
|
||||||
self.standing_on = node_ok(pos2, "air").name
|
self.standing_on = node_ok(pos2, "air").name
|
||||||
|
|
||||||
@ -671,7 +573,7 @@ function mob_class:do_env_damage()
|
|||||||
|
|
||||||
-- don't fall when on ignore, just stand still
|
-- don't fall when on ignore, just stand still
|
||||||
if self.standing_in == "ignore" then
|
if self.standing_in == "ignore" then
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
self.object:set_velocity(vector.zero())
|
||||||
-- wither rose effect
|
-- wither rose effect
|
||||||
elseif self.standing_in == "mcl_flowers:wither_rose" then
|
elseif self.standing_in == "mcl_flowers:wither_rose" then
|
||||||
mcl_potions.give_effect_by_level("withering", self.object, 2, 2)
|
mcl_potions.give_effect_by_level("withering", self.object, 2, 2)
|
||||||
@ -791,7 +693,7 @@ function mob_class:do_env_damage()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if drowning then
|
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)
|
mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil)
|
||||||
if self.breath <= 0 then
|
if self.breath <= 0 then
|
||||||
local dmg
|
local dmg
|
||||||
@ -808,7 +710,7 @@ function mob_class:do_env_damage()
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.breath = math.min(self.breath_max, self.breath + 1)
|
self.breath = min(self.breath_max, self.breath + 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -874,7 +776,7 @@ end
|
|||||||
|
|
||||||
function mob_class:damage_mob(reason,damage)
|
function mob_class:damage_mob(reason,damage)
|
||||||
if not self.health then return end
|
if not self.health then return end
|
||||||
damage = math.floor(damage)
|
damage = floor(damage)
|
||||||
if damage > 0 then
|
if damage > 0 then
|
||||||
self.health = self.health - damage
|
self.health = self.health - damage
|
||||||
|
|
||||||
@ -920,57 +822,45 @@ end
|
|||||||
-- falling and fall damage
|
-- falling and fall damage
|
||||||
-- returns true if mob died
|
-- returns true if mob died
|
||||||
function mob_class:falling(pos, moveresult)
|
function mob_class:falling(pos, moveresult)
|
||||||
if self.fly and self.state ~= "die" then
|
if self.fly and self.state ~= "die" then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.fall_speed then self.fall_speed = DEFAULT_FALL_SPEED 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 ~= nil then
|
||||||
if mcl_portals.nether_portal_cooloff(self.object) then
|
if mcl_portals.nether_portal_cooloff(self.object) then
|
||||||
return false -- mob has teleported through Nether portal - it's 99% not falling
|
return false -- mob has teleported through Nether portal - it's 99% not falling
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- floating in water (or falling)
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
if v then
|
|
||||||
local new_acceleration
|
|
||||||
|
|
||||||
if v.y > 0 then
|
|
||||||
-- apply gravity when moving up
|
|
||||||
new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0)
|
|
||||||
elseif 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
|
|
||||||
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]
|
local registered_node = minetest.registered_nodes[node_ok(pos).name]
|
||||||
|
|
||||||
if registered_node.groups.lava then
|
if registered_node.groups.lava then
|
||||||
if acc and self.floats_on_lava == 1 then
|
if self.floats_on_lava == 1 then
|
||||||
self.object:set_acceleration(vector.new(0, -self.fall_speed / (math.max(1, v.y) ^ 2), 0))
|
self.object:set_acceleration(vector.new(0, -self.fall_speed / max(1, v.y^2), 0))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- in water then float up
|
-- in water then float up
|
||||||
if registered_node.groups.water then
|
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
|
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 / (math.max(1, v.y) ^ 2), 0))
|
self.object:set_acceleration(vector.new(0, -self.fall_speed / max(1, v.y^2), 0))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- fall damage onto solid ground
|
-- fall damage onto solid ground
|
||||||
@ -994,13 +884,9 @@ end
|
|||||||
|
|
||||||
function mob_class:check_water_flow()
|
function mob_class:check_water_flow()
|
||||||
-- Add water flowing for mobs from mcl_item_entity
|
-- Add water flowing for mobs from mcl_item_entity
|
||||||
local p, node, nn, def
|
local p = self.object:get_pos()
|
||||||
p = self.object:get_pos()
|
local node = minetest.get_node_or_nil(p)
|
||||||
node = minetest.get_node_or_nil(p)
|
local def = node and minetest.registered_nodes[node.name]
|
||||||
if node then
|
|
||||||
nn = node.name
|
|
||||||
def = minetest.registered_nodes[nn]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Move item around on flowing liquids
|
-- Move item around on flowing liquids
|
||||||
if def and def.liquidtype == "flowing" then
|
if def and def.liquidtype == "flowing" then
|
||||||
@ -1015,14 +901,12 @@ function mob_class:check_water_flow()
|
|||||||
local f = 1.39
|
local f = 1.39
|
||||||
-- Set new item moving speed into the direciton of the liquid
|
-- Set new item moving speed into the direciton of the liquid
|
||||||
local newv = vector.multiply(vec, f)
|
local newv = vector.multiply(vec, f)
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
self.object:set_acceleration(vector.zero())
|
||||||
self.object:set_velocity({x = newv.x, y = -0.22, z = newv.z})
|
self.object:set_velocity(vector.new(newv.x, -0.22, newv.z))
|
||||||
|
|
||||||
self.physical_state = true
|
self.physical_state = true
|
||||||
self._flowing = true
|
self._flowing = true
|
||||||
self.object:set_properties({
|
self.object:set_properties({ physical = true })
|
||||||
physical = true
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
elseif self._flowing == true then
|
elseif self._flowing == true then
|
||||||
@ -1036,7 +920,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
|
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()
|
local rot = self.object:get_rotation()
|
||||||
if rot then
|
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)
|
self.object:set_rotation(rot)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
@ -43,10 +43,10 @@ pillager = {
|
|||||||
arrow = "mcl_bows:arrow_entity",
|
arrow = "mcl_bows:arrow_entity",
|
||||||
attack_type = "dogshoot", -- Alternate punching/shooting
|
attack_type = "dogshoot", -- Alternate punching/shooting
|
||||||
attack_npcs = true,
|
attack_npcs = true,
|
||||||
reach = 0, -- Punching max distance
|
reach = 2, -- Punching max distance
|
||||||
damage = 0, -- Punching damage
|
damage = 2, -- Punching damage
|
||||||
dogshoot_switch = 1, -- Start of shooting
|
dogshoot_switch = 1, -- Start of shooting
|
||||||
dogshoot_count_max = 5, -- Max time spent shooting (standing)
|
dogshoot_count_max = 4, -- Max time spent shooting (standing)
|
||||||
dogshoot_count2_max = 1, -- Max time spent punching (running)
|
dogshoot_count2_max = 1, -- Max time spent punching (running)
|
||||||
sounds = {
|
sounds = {
|
||||||
random = "mobs_mc_pillager_grunt2",
|
random = "mobs_mc_pillager_grunt2",
|
||||||
|
@ -83,7 +83,6 @@ mcl_mobs.register_mob("mobs_mc:shulker", {
|
|||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if math.floor(self.object:get_yaw()) ~=0 then
|
if math.floor(self.object:get_yaw()) ~=0 then
|
||||||
self.object:set_yaw(0)
|
self.object:set_yaw(0)
|
||||||
mcl_mobs:yaw(self, 0, 0, dtime)
|
|
||||||
end
|
end
|
||||||
if self.state == "attack" then
|
if self.state == "attack" then
|
||||||
self:set_animation("run")
|
self:set_animation("run")
|
||||||
|
@ -952,6 +952,9 @@ local function go_home(entity, sleep)
|
|||||||
entity.order = nil
|
entity.order = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
-- in case pathfinding fails, turn into the right direction anyways
|
||||||
|
local p = entity.object:get_pos()
|
||||||
|
entity:turn_in_direction(b.x - p.x, b.z - p.z, 8)
|
||||||
|
|
||||||
entity:gopath(b,function(entity,b)
|
entity:gopath(b,function(entity,b)
|
||||||
local b = entity._bed
|
local b = entity._bed
|
||||||
@ -1331,7 +1334,7 @@ local function do_work (self)
|
|||||||
--mcl_log("Jobsite not valid")
|
--mcl_log("Jobsite not valid")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if vector.distance(self.object:get_pos(),self._jobsite) < 2 then
|
if vector.distance(self.object:get_pos(),self._jobsite) < 1.5 then
|
||||||
--mcl_log("Made it to work ok callback!")
|
--mcl_log("Made it to work ok callback!")
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user