Compare commits
34 Commits
3e214793aa
...
95bcf86cbd
Author | SHA1 | Date |
---|---|---|
kno10 | 95bcf86cbd | |
kno10 | 1bc24999b4 | |
kno10 | 3adbd63fd6 | |
kno10 | 76d67ba941 | |
kno10 | 304b83d7ac | |
kno10 | 73a303ac83 | |
kno10 | f629a92e92 | |
kno10 | 1e5139a09d | |
kno10 | 3ff229224b | |
kno10 | 9b2d0d1b32 | |
kno10 | de60029d1a | |
kno10 | cf274dba4a | |
kno10 | fa9bde370b | |
kno10 | 935fc15770 | |
kno10 | 63198c6f9b | |
kno10 | 92aad6fd9a | |
kno10 | b58e5dedee | |
kno10 | 71fe8bf146 | |
kno10 | cf73d64550 | |
kno10 | a0bce0d2fd | |
kno10 | 6f4a82cf9e | |
kno10 | 995972bb40 | |
kno10 | f0add79a8d | |
kno10 | d060ec65a9 | |
kno10 | 4b47eb304e | |
kno10 | 6d7ceb37ce | |
kno10 | 2d4f5a4518 | |
marro | 4dc5d0939c | |
the-real-herowl | 32b334322b | |
grorp | 88c3c4558b | |
grorp | 3954acdfb7 | |
grorp | 02b354f54a | |
grorp | cb624fe1d9 | |
grorp | bd9ab16762 |
|
@ -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
|
||||||
|
@ -384,14 +295,10 @@ function mob_class:outside_limits()
|
||||||
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
|
||||||
|
|
||||||
|
@ -410,22 +317,13 @@ local function on_step_work(self, dtime, moveresult)
|
||||||
|
|
||||||
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,29 +340,20 @@ 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
|
||||||
|
@ -473,20 +362,16 @@ 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)")
|
||||||
|
@ -499,34 +384,24 @@ local on_step_error_handler = function ()
|
||||||
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
|
|
||||||
else
|
|
||||||
warn_user_error()
|
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
|
end
|
||||||
log_error(dump(retVal), dump(pos), dump(self))
|
log_error(dump(retVal), dump(pos), dump(self))
|
||||||
end
|
end
|
||||||
else
|
|
||||||
return on_step_work (self, dtime, moveresult)
|
|
||||||
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"
|
||||||
|
@ -56,20 +47,17 @@ 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
|
||||||
|
@ -592,17 +509,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
||||||
|
|
||||||
|
|
||||||
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,7 +641,7 @@ 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")
|
||||||
|
@ -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,13 +662,16 @@ 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()
|
||||||
|
if hp and sp then
|
||||||
|
self:turn_in_direction(sp.x - hp.x, sp.z - hp.z, 1)
|
||||||
self:set_velocity(self.run_velocity)
|
self:set_velocity(self.run_velocity)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
self.state = "runaway"
|
self.state = "runaway"
|
||||||
self.runaway_timer = 0
|
self.runaway_timer = 0
|
||||||
|
@ -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"
|
||||||
|
@ -880,15 +768,12 @@ 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
|
||||||
|
@ -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
|
||||||
|
@ -1102,8 +940,7 @@ function mob_class:do_states_attack (dtime)
|
||||||
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
|
||||||
|
|
|
@ -5,7 +5,11 @@ local validate_vector = mcl_util.validate_vector
|
||||||
local active_particlespawners = {}
|
local active_particlespawners = {}
|
||||||
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
||||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||||
local PI_THIRD = math.pi / 3 -- 60 degrees
|
local PI = math.pi
|
||||||
|
local TWOPI = math.pi * 2
|
||||||
|
local PI_HALF = math.pi * 0.5 -- 90 degrees
|
||||||
|
local MAX_PITCH = math.pi * 0.45 -- about 80 degrees
|
||||||
|
local MAX_YAW = math.pi * 0.66 -- about 120 degrees
|
||||||
|
|
||||||
local PATHFINDING = "gowp"
|
local PATHFINDING = "gowp"
|
||||||
|
|
||||||
|
@ -246,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"
|
||||||
|
@ -256,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
|
||||||
|
|
||||||
|
@ -275,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,
|
||||||
|
@ -290,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
|
||||||
|
@ -348,13 +336,8 @@ function mob_class:check_head_swivel(dtime)
|
||||||
|
|
||||||
local locked_object = self._locked_object
|
local locked_object = self._locked_object
|
||||||
if locked_object and (locked_object:is_player() or locked_object:get_luaentity()) and locked_object:get_hp() > 0 then
|
if locked_object and (locked_object:is_player() or locked_object:get_luaentity()) and locked_object:get_hp() > 0 then
|
||||||
local _locked_object_eye_height = 1.5
|
local _locked_object_eye_height = (locked_object:is_player() and locked_object:get_properties().eye_height * 0.8) -- food in hands of player
|
||||||
if locked_object:is_player() then
|
or (locked_object:get_luaentity() and locked_object:get_luaentity().head_eye_height) or 1.5
|
||||||
_locked_object_eye_height = locked_object:get_properties().eye_height
|
|
||||||
elseif locked_object:get_luaentity() then
|
|
||||||
_locked_object_eye_height = locked_object:get_luaentity().head_eye_height
|
|
||||||
end
|
|
||||||
if _locked_object_eye_height then
|
|
||||||
local self_rot = self.object:get_rotation()
|
local self_rot = self.object:get_rotation()
|
||||||
-- If a mob is attached, should we really be messing with what they are looking at?
|
-- If a mob is attached, should we really be messing with what they are looking at?
|
||||||
-- Should this be excluded?
|
-- Should this be excluded?
|
||||||
|
@ -363,43 +346,52 @@ function mob_class:check_head_swivel(dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
local ps = self.object:get_pos()
|
local ps = self.object:get_pos()
|
||||||
ps.y = ps.y + self.head_eye_height * .7
|
ps.y = ps.y + self.head_eye_height -- why here, instead of below? * .7
|
||||||
local pt = locked_object:get_pos()
|
local pt = locked_object:get_pos()
|
||||||
pt.y = pt.y + _locked_object_eye_height
|
pt.y = pt.y + _locked_object_eye_height
|
||||||
local dir = vector.direction(ps, pt)
|
local dir = vector.direction(ps, pt) -- is (pt-ps):normalize()
|
||||||
local mob_yaw = self_rot.y + math.atan2(dir.x, dir.z) + self.head_yaw_offset
|
local mob_yaw = math.atan2(dir.x, dir.z)
|
||||||
local mob_pitch = math.asin(-dir.y) * self.head_pitch_multiplier
|
local mob_pitch = -math.asin(dir.y) * (self.head_pitch_multiplier or 1) -- allow axis inversion
|
||||||
|
|
||||||
if (mob_yaw < -PI_THIRD or mob_yaw > PI_THIRD) and not (self.attack and self.state == "attack" and not self.runaway) then
|
mob_yaw = mob_yaw + self_rot.y -- to relative orientation
|
||||||
newr = vector.multiply(oldr, 0.9)
|
while mob_yaw > PI do mob_yaw = mob_yaw - TWOPI end
|
||||||
elseif self.attack and self.state == "attack" and not self.runaway then
|
while mob_yaw < -PI do mob_yaw = mob_yaw + TWOPI end
|
||||||
|
mob_yaw = mob_yaw * 0.8 -- lessen the effect so it become less staring
|
||||||
|
local max_yaw = self.head_max_yaw or MAX_YAW
|
||||||
|
mob_yaw = (mob_yaw < -max_yaw and -max_yaw) or (mob_yaw < max_yaw and mob_yaw) or max_yaw -- avoid twisting the neck
|
||||||
|
|
||||||
|
mob_pitch = mob_pitch * 0.8 -- make it less obvious that this is computed
|
||||||
|
local max_pitch = self.head_max_pitch or MAX_PITCH
|
||||||
|
mob_pitch = (mob_pitch < -max_pitch and -max_pitch) or (mob_pitch < max_pitch and mob_pitch) or max_pitch
|
||||||
|
|
||||||
|
local smoothing = (self.state == "attack" and self.attack and 0.25) or 0.05
|
||||||
|
local old_pitch = oldr.x
|
||||||
|
local old_yaw = (self.head_yaw == "y" and oldr.y or -oldr.z) - self.head_yaw_offset
|
||||||
|
-- to -pi:+pi range, so we rotate over 0 when interpolating:
|
||||||
|
while old_yaw > PI do old_yaw = old_yaw - TWOPI end
|
||||||
|
while old_yaw < -PI do old_yaw = old_yaw + TWOPI end
|
||||||
|
mob_pitch, mob_yaw = (mob_pitch-old_pitch)*smoothing+old_pitch, (mob_yaw-old_yaw)*smoothing+old_yaw
|
||||||
|
-- apply the yaw to the mob
|
||||||
|
mob_yaw = mob_yaw + self.head_yaw_offset
|
||||||
if self.head_yaw == "y" then
|
if self.head_yaw == "y" then
|
||||||
newr = vector.new(mob_pitch, mob_yaw, 0)
|
newr = vector.new(mob_pitch, mob_yaw, 0)
|
||||||
elseif self.head_yaw == "z" then
|
elseif self.head_yaw == "z" then
|
||||||
newr = vector.new(mob_pitch, 0, -mob_yaw)
|
newr = vector.new(mob_pitch, 0, -mob_yaw) -- z yaw is opposite direction
|
||||||
end
|
end
|
||||||
else
|
elseif math.abs(oldr.x) + math.abs(oldr.y) + math.abs(oldr.z) > 0.05 then
|
||||||
if self.head_yaw == "y" then
|
newr = vector.multiply(oldr, 0.9) -- smooth stop looking
|
||||||
newr = vector.new((mob_pitch-oldr.x)*.3+oldr.x, (mob_yaw-oldr.y)*.3+oldr.y, 0)
|
|
||||||
elseif self.head_yaw == "z" then
|
|
||||||
newr = vector.new((mob_pitch-oldr.x)*.3+oldr.x, 0, ((mob_yaw-oldr.y)*.3+oldr.y)*-3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif not locked_object and math.abs(oldr.y) > 0.05 and math.abs(oldr.x) < 0.05 then
|
|
||||||
newr = vector.multiply(oldr, 0.9)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 0.02 is about 1.14 degrees tolerance, to update less often
|
-- 0.02 is about 1.14 degrees tolerance, to update less often
|
||||||
local newp = vector.new(0, self.bone_eye_height, self.horizontal_head_height)
|
if math.abs(oldr.x-newr.x) + math.abs(oldr.y-newr.y) + math.abs(oldr.z-newr.z) < 0.02 and vector.equals(oldp, vector.zero()) then return end
|
||||||
if math.abs(oldr.x-newr.x) + math.abs(oldr.y-newr.y) + math.abs(oldr.z-newr.z) < 0.02 and vector.equals(oldp, newp) then return end
|
|
||||||
if self.object.get_bone_override then -- minetest >= 5.9
|
if self.object.get_bone_override then -- minetest >= 5.9
|
||||||
self.object:set_bone_override(self.head_swivel, {
|
self.object:set_bone_override(self.head_swivel, {
|
||||||
position = { vec = newp, absolute = true },
|
position = { vec = self.head_bone_position, absolute = true },
|
||||||
rotation = { vec = newr, absolute = true } })
|
rotation = { vec = newr, absolute = true, interpolation = 0.1 } })
|
||||||
else -- minetest < 5.9
|
else -- minetest < 5.9
|
||||||
-- old API uses degrees not radians
|
-- old API uses degrees not radians and absolute positions
|
||||||
self.object:set_bone_position(self.head_swivel, newp, vector.apply(newr, math.deg))
|
self.object:set_bone_position(self.head_swivel, self.head_bone_position, vector.apply(newr, math.deg))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
@ -143,11 +139,12 @@ function mcl_mobs.register_mob(name, def)
|
||||||
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
||||||
head_yaw_offset = math.rad(def.head_yaw_offset or 0), -- for wonkey model bones
|
head_yaw_offset = math.rad(def.head_yaw_offset or 0), -- for wonkey model bones
|
||||||
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
|
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
|
||||||
bone_eye_height = def.bone_eye_height or 1.4, -- head bone offset
|
head_eye_height = def.head_eye_height or 1, -- how high approximately the mobs eyes are from the ground to tell the mob how high to look up at the player
|
||||||
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how hight aproximatly the mobs head is fromm the ground to tell the mob how high to look up at the player
|
head_max_yaw = def.head_max_yaw, -- how far the mob may turn the head
|
||||||
|
head_max_pitch = def.head_max_pitch, -- how far up and down the mob may pitch the head
|
||||||
|
head_bone_position = def.head_bone_position or { 0, def.bone_eye_height or 1.4, def.horizontal_head_height or 0},
|
||||||
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
||||||
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
||||||
horizontal_head_height = def.horizontal_head_height or 0,
|
|
||||||
wears_armor = def.wears_armor, -- a number value used to index texture slot for armor
|
wears_armor = def.wears_armor, -- a number value used to index texture slot for armor
|
||||||
stepheight = def.stepheight or 0.6,
|
stepheight = def.stepheight or 0.6,
|
||||||
name = name,
|
name = name,
|
||||||
|
|
|
@ -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
|
end
|
||||||
|
|
||||||
if wp and not enriched_path then
|
-- we treat ignore as solid, as we cannot path there
|
||||||
mcl_log("Wp but not enriched")
|
local function is_solid(pos)
|
||||||
enriched_path = generate_enriched_path(wp)
|
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||||
end
|
return (not ndef) or ndef.walkable
|
||||||
return enriched_path
|
|
||||||
end
|
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,27 +254,93 @@ 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)
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
|
@ -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
|
end
|
||||||
|
|
||||||
return 0
|
|
||||||
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
|
||||||
|
self.is_Fleckenstein = is_Fleckenstein
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Relative turn, primarily for random turning
|
||||||
|
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||||
|
function mob_class:turn_by(angle, delay, dtime)
|
||||||
|
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)
|
||||||
local function shortest_term_of_yaw_rotation(self, rot_origin, rot_target, nums)
|
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||||
|
function mob_class:turn_in_direction(dx, dz, delay, dtime)
|
||||||
if not rot_origin or not rot_target then
|
if abs(dx) == 0 and abs(dz) == 0 then return self.object:get_yaw() + self.rotate end
|
||||||
return
|
return self:set_yaw(-atan2(dx, dz) - self.rotate, delay, dtime) + self.rotate
|
||||||
end
|
end
|
||||||
|
|
||||||
rot_origin = math.deg(rot_origin)
|
|
||||||
rot_target = math.deg(rot_target)
|
|
||||||
|
|
||||||
if rot_origin < rot_target then
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- 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
|
||||||
|
@ -672,7 +574,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
|
||||||
|
|
||||||
|
@ -681,7 +583,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)
|
||||||
|
@ -799,7 +701,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
|
||||||
|
@ -816,7 +718,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
|
||||||
|
|
||||||
|
@ -882,7 +784,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
|
||||||
|
|
||||||
|
@ -928,57 +830,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
|
||||||
|
@ -1002,13 +892,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
|
||||||
|
@ -1023,14 +909,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
|
||||||
|
@ -1044,7 +928,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
|
||||||
|
|
|
@ -12,9 +12,8 @@ local axolotl = {
|
||||||
xp_max = 7,
|
xp_max = 7,
|
||||||
|
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = -1,
|
head_eye_height = 0.5,
|
||||||
head_eye_height = -0.5,
|
head_bone_position = vector.new( 0, -1, 0 ), -- for minetest <= 5.8
|
||||||
horizontal_head_height = 0,
|
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
|
|
||||||
|
|
|
@ -26,14 +26,14 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
|
||||||
xp_min = 10,
|
xp_min = 10,
|
||||||
xp_max = 10,
|
xp_max = 10,
|
||||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.79, 0.3},
|
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.79, 0.3},
|
||||||
rotate = -180,
|
rotate = 180,
|
||||||
|
head_yaw_offset = 180,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_blaze.b3d",
|
mesh = "mobs_mc_blaze.b3d",
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 4,
|
head_eye_height = 1.4,
|
||||||
head_eye_height = 3.5,
|
head_bone_position = vector.new( 0, 4, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
head_yaw_offset = 180,
|
|
||||||
head_pitch_multiplier=-1,
|
head_pitch_multiplier=-1,
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_blaze.png"},
|
{"mobs_mc_blaze.png"},
|
||||||
|
|
|
@ -21,9 +21,8 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
||||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
|
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
|
||||||
floats = 1,
|
floats = 1,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 4,
|
head_eye_height = 0.5,
|
||||||
head_eye_height = 1.5,
|
head_bone_position = vector.new(0, 4, -.3), -- for minetest <= 5.8
|
||||||
horizontal_head_height = -.3,
|
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
visual_size = {x=1,y=1},
|
visual_size = {x=1,y=1},
|
||||||
|
|
|
@ -22,9 +22,8 @@ local cow_def = {
|
||||||
"blank.png",
|
"blank.png",
|
||||||
}, },
|
}, },
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 10,
|
|
||||||
head_eye_height = 1.1,
|
head_eye_height = 1.1,
|
||||||
horizontal_head_height=-1.8,
|
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 2,
|
curiosity = 2,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
|
|
|
@ -25,7 +25,8 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_iron_golem.b3d",
|
mesh = "mobs_mc_iron_golem.b3d",
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 3.38,
|
head_eye_height = 2.5,
|
||||||
|
head_bone_position = vector.new( 0, 3.38, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_iron_golem.png"},
|
{"mobs_mc_iron_golem.png"},
|
||||||
|
|
|
@ -60,11 +60,10 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
||||||
spawn_in_group = 4, -- was 6 nerfed until we can cap them properly locally. this is a group size, not a per spawn attempt
|
spawn_in_group = 4, -- was 6 nerfed until we can cap them properly locally. this is a group size, not a per spawn attempt
|
||||||
|
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 11,
|
head_eye_height = 1.5,
|
||||||
head_eye_height = 3,
|
|
||||||
horizontal_head_height=0,
|
|
||||||
curiosity = 60,
|
|
||||||
head_yaw = "z",
|
head_yaw = "z",
|
||||||
|
head_bone_position = vector.new( 0, 11, 0 ), -- for minetest <= 5.8
|
||||||
|
curiosity = 60,
|
||||||
|
|
||||||
hp_min = 15,
|
hp_min = 15,
|
||||||
hp_max = 30,
|
hp_max = 30,
|
||||||
|
|
|
@ -37,9 +37,8 @@ local ocelot = {
|
||||||
xp_min = 1,
|
xp_min = 1,
|
||||||
xp_max = 3,
|
xp_max = 3,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 6.2,
|
|
||||||
head_eye_height = 0.4,
|
head_eye_height = 0.4,
|
||||||
horizontal_head_height=-0,
|
head_bone_position = vector.new( 0, 6.2, 0 ), -- for minetest <= 5.8
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
curiosity = 4,
|
curiosity = 4,
|
||||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.69, 0.3},
|
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.69, 0.3},
|
||||||
|
|
|
@ -136,8 +136,7 @@ mcl_mobs.register_mob("mobs_mc:parrot", {
|
||||||
xp_min = 1,
|
xp_min = 1,
|
||||||
xp_max = 3,
|
xp_max = 3,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 1.1,
|
head_bone_position = vector.new( 0, 1.1, 0 ), -- for minetest <= 5.8
|
||||||
horizontal_head_height=0,
|
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.89, 0.25},
|
collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.89, 0.25},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
|
|
|
@ -20,9 +20,8 @@ mcl_mobs.register_mob("mobs_mc:pig", {
|
||||||
"blank.png", -- saddle
|
"blank.png", -- saddle
|
||||||
}},
|
}},
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 7.5,
|
head_eye_height = 0.7,
|
||||||
head_eye_height = 0.8,
|
head_bone_position = vector.new( 0, 7.5, -1 ), -- for minetest <= 5.8
|
||||||
horizontal_head_height=-1,
|
|
||||||
curiosity = 3,
|
curiosity = 3,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
|
|
|
@ -252,7 +252,7 @@ local zombified_piglin = {
|
||||||
damage = 9,
|
damage = 9,
|
||||||
reach = 2,
|
reach = 2,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 2.4,
|
head_bone_position = vector.new( 0, 2.4, 0 ), -- for minetest <= 5.8
|
||||||
head_eye_height = 1.4,
|
head_eye_height = 1.4,
|
||||||
curiosity = 15,
|
curiosity = 15,
|
||||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3}, -- same
|
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3}, -- same
|
||||||
|
@ -325,6 +325,7 @@ mcl_mobs.register_mob("mobs_mc:zombified_piglin", zombified_piglin)
|
||||||
local baby_zombified_piglin = table.copy(zombified_piglin)
|
local baby_zombified_piglin = table.copy(zombified_piglin)
|
||||||
baby_zombified_piglin.description = S("Baby Zombie Piglin")
|
baby_zombified_piglin.description = S("Baby Zombie Piglin")
|
||||||
baby_zombified_piglin.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.94, 0.25}
|
baby_zombified_piglin.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.94, 0.25}
|
||||||
|
baby_zombified_piglin.head_eye_height = 0.8
|
||||||
baby_zombified_piglin.xp_min = 13
|
baby_zombified_piglin.xp_min = 13
|
||||||
baby_zombified_piglin.xp_max = 13
|
baby_zombified_piglin.xp_max = 13
|
||||||
baby_zombified_piglin.textures = {
|
baby_zombified_piglin.textures = {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -25,9 +25,8 @@ mcl_mobs.register_mob("mobs_mc:polar_bear", {
|
||||||
{"mobs_mc_polarbear.png"},
|
{"mobs_mc_polarbear.png"},
|
||||||
},
|
},
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 2.6,
|
|
||||||
head_eye_height = 1,
|
head_eye_height = 1,
|
||||||
horizontal_head_height = 0,
|
head_bone_position = vector.new( 0, 2.6, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 20,
|
curiosity = 20,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
visual_size = {x=3.0, y=3.0},
|
visual_size = {x=3.0, y=3.0},
|
||||||
|
|
|
@ -16,9 +16,8 @@ local rabbit = {
|
||||||
xp_max = 3,
|
xp_max = 3,
|
||||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 2,
|
head_eye_height = 0.35,
|
||||||
head_eye_height = 0.5,
|
head_bone_position = vector.new( 0, 2, -.3 ), -- for minetest <= 5.8
|
||||||
horizontal_head_height = -.3,
|
|
||||||
curiosity = 20,
|
curiosity = 20,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
|
|
|
@ -65,9 +65,8 @@ mcl_mobs.register_mob("mobs_mc:sheep", {
|
||||||
xp_max = 3,
|
xp_max = 3,
|
||||||
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.29, 0.45},
|
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.29, 0.45},
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 3.3,
|
head_eye_height = 1.0,
|
||||||
head_eye_height = 1.1,
|
head_bone_position = vector.new( 0, 3.3, -.7 ), -- for minetest <= 5.8
|
||||||
horizontal_head_height=-.7,
|
|
||||||
curiosity = 6,
|
curiosity = 6,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -26,7 +26,8 @@ local skeleton = {
|
||||||
pathfinding = 1,
|
pathfinding = 1,
|
||||||
group_attack = true,
|
group_attack = true,
|
||||||
head_swivel = "Head_Control",
|
head_swivel = "Head_Control",
|
||||||
bone_eye_height = 2.38,
|
head_eye_height = 1.5,
|
||||||
|
head_bone_position = vector.new( 0, 2.38, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 6,
|
curiosity = 6,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_skeleton.b3d",
|
mesh = "mobs_mc_skeleton.b3d",
|
||||||
|
|
|
@ -25,7 +25,8 @@ mcl_mobs.register_mob("mobs_mc:witherskeleton", {
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_witherskeleton.b3d",
|
mesh = "mobs_mc_witherskeleton.b3d",
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 2.38,
|
head_eye_height = 1.5,
|
||||||
|
head_bone_position = vector.new( 0, 2.38, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 60,
|
curiosity = 60,
|
||||||
textures = {
|
textures = {
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,7 +63,8 @@ local spider = {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
head_swivel = "Head_Control",
|
head_swivel = "Head_Control",
|
||||||
bone_eye_height = 1,
|
head_eye_height = 0.6,
|
||||||
|
head_bone_position = vector.new( 0, 1, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7},
|
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7},
|
||||||
|
|
|
@ -75,8 +75,8 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "vl_stalker.b3d",
|
mesh = "vl_stalker.b3d",
|
||||||
-- head_swivel = "Head_Control",
|
-- head_swivel = "Head_Control",
|
||||||
bone_eye_height = 2.35,
|
head_eye_height = 1.2;
|
||||||
head_eye_height = 1.8;
|
head_bone_position = vector.new( 0, 2.35, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 2,
|
curiosity = 2,
|
||||||
textures = {
|
textures = {
|
||||||
{get_texture({}),
|
{get_texture({}),
|
||||||
|
|
|
@ -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
|
||||||
|
@ -2110,8 +2113,8 @@ mcl_mobs.register_mob("mobs_mc:villager", {
|
||||||
hp_min = 20,
|
hp_min = 20,
|
||||||
hp_max = 20,
|
hp_max = 20,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 6.3,
|
head_eye_height = 1.5,
|
||||||
head_eye_height = 2.2,
|
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
runaway = true,
|
runaway = true,
|
||||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
|
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
|
||||||
|
|
|
@ -25,8 +25,8 @@ mcl_mobs.register_mob("mobs_mc:evoker", {
|
||||||
xp_min = 10,
|
xp_min = 10,
|
||||||
xp_max = 10,
|
xp_max = 10,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 6.3,
|
head_eye_height = 1.5,
|
||||||
head_eye_height = 2.2,
|
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.95, 0.4},
|
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.95, 0.4},
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
|
|
|
@ -34,8 +34,8 @@ mcl_mobs.register_mob("mobs_mc:illusioner", {
|
||||||
"mcl_bows_bow.png",
|
"mcl_bows_bow.png",
|
||||||
}, },
|
}, },
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 2.2,
|
head_eye_height = 1.5,
|
||||||
head_eye_height = 2.2,
|
head_bone_position = vector.new( 0, 2.2, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
sounds = {
|
sounds = {
|
||||||
-- TODO: more sounds
|
-- TODO: more sounds
|
||||||
|
|
|
@ -24,8 +24,8 @@ mcl_mobs.register_mob("mobs_mc:vindicator", {
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_vindicator.b3d",
|
mesh = "mobs_mc_vindicator.b3d",
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 2.2,
|
head_eye_height = 1.5,
|
||||||
head_eye_height = 2.2,
|
head_bone_position = vector.new( 0, 2.2, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
textures = {
|
textures = {
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,7 +40,7 @@ mcl_mobs.register_mob("mobs_mc:villager_zombie", {
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = "mobs_mc_villager_zombie.b3d",
|
mesh = "mobs_mc_villager_zombie.b3d",
|
||||||
head_swivel = "Head_Control",
|
head_swivel = "Head_Control",
|
||||||
bone_eye_height = 2.35,
|
head_bone_position = vector.new( 0, 2.35, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 2,
|
curiosity = 2,
|
||||||
textures = {
|
textures = {
|
||||||
{"mobs_mc_zombie_butcher.png"},
|
{"mobs_mc_zombie_butcher.png"},
|
||||||
|
|
|
@ -27,8 +27,8 @@ local wolf = {
|
||||||
},
|
},
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 3.5,
|
head_eye_height = 0.5,
|
||||||
head_eye_height = 1.1,
|
head_bone_position = vector.new( 0, 3.5, 0 ), -- for minetest <= 5.8
|
||||||
horizontal_head_height=0,
|
horizontal_head_height=0,
|
||||||
curiosity = 3,
|
curiosity = 3,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
|
|
|
@ -54,8 +54,8 @@ local zombie = {
|
||||||
xp_min = 5,
|
xp_min = 5,
|
||||||
xp_max = 5,
|
xp_max = 5,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 6.3,
|
head_eye_height = 1.4,
|
||||||
head_eye_height = 2.2,
|
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||||
curiosity = 7,
|
curiosity = 7,
|
||||||
head_pitch_multiplier=-1,
|
head_pitch_multiplier=-1,
|
||||||
breath_max = -1,
|
breath_max = -1,
|
||||||
|
@ -110,6 +110,7 @@ mcl_mobs.register_mob("mobs_mc:zombie", zombie)
|
||||||
|
|
||||||
local baby_zombie = table.copy(zombie)
|
local baby_zombie = table.copy(zombie)
|
||||||
baby_zombie.description = S("Baby Zombie")
|
baby_zombie.description = S("Baby Zombie")
|
||||||
|
baby_zombie.head_eye_height = 0.8
|
||||||
baby_zombie.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.98, 0.25}
|
baby_zombie.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.98, 0.25}
|
||||||
baby_zombie.xp_min = 12
|
baby_zombie.xp_min = 12
|
||||||
baby_zombie.xp_max = 12
|
baby_zombie.xp_max = 12
|
||||||
|
|
|
@ -418,6 +418,18 @@ minetest.register_on_joinplayer(function(player)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
---@param player mt.PlayerObjectRef
|
||||||
|
local function is_touch_enabled(playername)
|
||||||
|
-- Minetest < 5.7.0 support
|
||||||
|
if not minetest.get_player_window_information then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local window = minetest.get_player_window_information(playername)
|
||||||
|
-- Always return a boolean (not nil) to avoid false-negatives when
|
||||||
|
-- comparing to a boolean later.
|
||||||
|
return window and window.touch_controls or false
|
||||||
|
end
|
||||||
|
|
||||||
---@param player mt.PlayerObjectRef
|
---@param player mt.PlayerObjectRef
|
||||||
function mcl_inventory.set_creative_formspec(player)
|
function mcl_inventory.set_creative_formspec(player)
|
||||||
local playername = player:get_player_name()
|
local playername = player:get_player_name()
|
||||||
|
@ -566,8 +578,10 @@ function mcl_inventory.set_creative_formspec(player)
|
||||||
bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png"
|
bg_img = "crafting_creative_inactive" .. button_bg_postfix[this_tab] .. ".png"
|
||||||
end
|
end
|
||||||
return table.concat({
|
return table.concat({
|
||||||
"style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=;noclip=true]",
|
"style[" .. this_tab .. ";border=false;bgimg=;bgimg_pressed=]",
|
||||||
"image[" .. offset[this_tab] .. ";1.5,1.44;" .. bg_img .. "]",
|
"style[" .. this_tab .. "_outer;border=false;bgimg=" .. bg_img ..
|
||||||
|
";bgimg_pressed=" .. bg_img .. "]",
|
||||||
|
"button[" .. offset[this_tab] .. ";1.5,1.44;" .. this_tab .. "_outer;]",
|
||||||
"item_image_button[" .. boffset[this_tab] .. ";1,1;" .. tab_icon[this_tab] .. ";" .. this_tab .. ";]",
|
"item_image_button[" .. boffset[this_tab] .. ";1,1;" .. tab_icon[this_tab] .. ";" .. this_tab .. ";]",
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
@ -577,11 +591,21 @@ function mcl_inventory.set_creative_formspec(player)
|
||||||
caption = "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, filtername[name])) .. "]"
|
caption = "label[0.375,0.375;" .. F(C(mcl_formspec.label_color, filtername[name])) .. "]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local touch_enabled = is_touch_enabled(playername)
|
||||||
|
players[playername].last_touch_enabled = touch_enabled
|
||||||
|
|
||||||
local formspec = table.concat({
|
local formspec = table.concat({
|
||||||
"formspec_version[6]",
|
"formspec_version[6]",
|
||||||
"size[13,8.75]",
|
-- Original formspec height was 8.75, increased to include tab buttons.
|
||||||
|
-- This avoids tab buttons going off-screen with high scaling values.
|
||||||
|
"size[13,11.43]",
|
||||||
|
-- Use as much space as possible on mobile - the tab buttons are a lot
|
||||||
|
-- of padding already.
|
||||||
|
touch_enabled and "padding[-0.015,-0.015]" or "",
|
||||||
|
|
||||||
"style_type[image;noclip=true]",
|
"no_prepend[]", mcl_vars.gui_nonbg, mcl_vars.gui_bg_color,
|
||||||
|
"background9[0,1.34;13,8.75;mcl_base_textures_background9.png;;7]",
|
||||||
|
"container[0,1.34]",
|
||||||
|
|
||||||
-- Hotbar
|
-- Hotbar
|
||||||
mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1),
|
mcl_formspec.get_itemslot_bg_v4(0.375, 7.375, 9, 1),
|
||||||
|
@ -638,6 +662,7 @@ function mcl_inventory.set_creative_formspec(player)
|
||||||
"set_focus[search;true]",
|
"set_focus[search;true]",
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
formspec = formspec .. "container_end[]"
|
||||||
if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end
|
if pagenum then formspec = formspec .. "p" .. tostring(pagenum) end
|
||||||
player:set_inventory_formspec(formspec)
|
player:set_inventory_formspec(formspec)
|
||||||
end
|
end
|
||||||
|
@ -655,54 +680,54 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
|
|
||||||
if fields.blocks then
|
if fields.blocks or fields.blocks_outer then
|
||||||
if players[name].page == "blocks" then return end
|
if players[name].page == "blocks" then return end
|
||||||
set_inv_page("blocks", player)
|
set_inv_page("blocks", player)
|
||||||
page = "blocks"
|
page = "blocks"
|
||||||
elseif fields.deco then
|
elseif fields.deco or fields.deco_outer then
|
||||||
if players[name].page == "deco" then return end
|
if players[name].page == "deco" then return end
|
||||||
set_inv_page("deco", player)
|
set_inv_page("deco", player)
|
||||||
page = "deco"
|
page = "deco"
|
||||||
elseif fields.redstone then
|
elseif fields.redstone or fields.redstone_outer then
|
||||||
if players[name].page == "redstone" then return end
|
if players[name].page == "redstone" then return end
|
||||||
set_inv_page("redstone", player)
|
set_inv_page("redstone", player)
|
||||||
page = "redstone"
|
page = "redstone"
|
||||||
elseif fields.rail then
|
elseif fields.rail or fields.rail_outer then
|
||||||
if players[name].page == "rail" then return end
|
if players[name].page == "rail" then return end
|
||||||
set_inv_page("rail", player)
|
set_inv_page("rail", player)
|
||||||
page = "rail"
|
page = "rail"
|
||||||
elseif fields.misc then
|
elseif fields.misc or fields.misc_outer then
|
||||||
if players[name].page == "misc" then return end
|
if players[name].page == "misc" then return end
|
||||||
set_inv_page("misc", player)
|
set_inv_page("misc", player)
|
||||||
page = "misc"
|
page = "misc"
|
||||||
elseif fields.nix then
|
elseif fields.nix or fields.nix_outer then
|
||||||
set_inv_page("all", player)
|
set_inv_page("all", player)
|
||||||
page = "nix"
|
page = "nix"
|
||||||
elseif fields.food then
|
elseif fields.food or fields.food_outer then
|
||||||
if players[name].page == "food" then return end
|
if players[name].page == "food" then return end
|
||||||
set_inv_page("food", player)
|
set_inv_page("food", player)
|
||||||
page = "food"
|
page = "food"
|
||||||
elseif fields.tools then
|
elseif fields.tools or fields.tools_outer then
|
||||||
if players[name].page == "tools" then return end
|
if players[name].page == "tools" then return end
|
||||||
set_inv_page("tools", player)
|
set_inv_page("tools", player)
|
||||||
page = "tools"
|
page = "tools"
|
||||||
elseif fields.combat then
|
elseif fields.combat or fields.combat_outer then
|
||||||
if players[name].page == "combat" then return end
|
if players[name].page == "combat" then return end
|
||||||
set_inv_page("combat", player)
|
set_inv_page("combat", player)
|
||||||
page = "combat"
|
page = "combat"
|
||||||
elseif fields.mobs then
|
elseif fields.mobs or fields.mobs_outer then
|
||||||
if players[name].page == "mobs" then return end
|
if players[name].page == "mobs" then return end
|
||||||
set_inv_page("mobs", player)
|
set_inv_page("mobs", player)
|
||||||
page = "mobs"
|
page = "mobs"
|
||||||
elseif fields.brew then
|
elseif fields.brew or fields.brew_outer then
|
||||||
if players[name].page == "brew" then return end
|
if players[name].page == "brew" then return end
|
||||||
set_inv_page("brew", player)
|
set_inv_page("brew", player)
|
||||||
page = "brew"
|
page = "brew"
|
||||||
elseif fields.matr then
|
elseif fields.matr or fields.matr_outer then
|
||||||
if players[name].page == "matr" then return end
|
if players[name].page == "matr" then return end
|
||||||
set_inv_page("matr", player)
|
set_inv_page("matr", player)
|
||||||
page = "matr"
|
page = "matr"
|
||||||
elseif fields.inv then
|
elseif fields.inv or fields.inv_outer then
|
||||||
if players[name].page == "inv" then return end
|
if players[name].page == "inv" then return end
|
||||||
page = "inv"
|
page = "inv"
|
||||||
elseif fields.search == "" and not fields.creative_next and not fields.creative_prev then
|
elseif fields.search == "" and not fields.creative_next and not fields.creative_prev then
|
||||||
|
@ -818,3 +843,19 @@ minetest.register_on_player_inventory_action(function(player, action, inventory,
|
||||||
player:get_inventory():set_stack("main", inventory_info.index, stack)
|
player:get_inventory():set_stack("main", inventory_info.index, stack)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- This is necessary because get_player_window_information may return nil in
|
||||||
|
-- on_joinplayer.
|
||||||
|
-- (Also, Minetest plans to add support for toggling touchscreen mode in-game.)
|
||||||
|
minetest.register_globalstep(function(dtime)
|
||||||
|
for _, player in pairs(minetest.get_connected_players()) do
|
||||||
|
local name = player:get_player_name()
|
||||||
|
|
||||||
|
if minetest.is_creative_enabled(name) then
|
||||||
|
local touch_enabled = is_touch_enabled(name)
|
||||||
|
if touch_enabled ~= players[name].last_touch_enabled then
|
||||||
|
mcl_inventory.set_creative_formspec(player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# textdomain: mcl_bone_meal
|
# textdomain: mcl_bone_meal
|
||||||
Bone Meal=Farine d'Os
|
Bone Meal=Farine d'Os
|
||||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La farine d'os est une teinture blanche et est également utile comme engrais pour accélérer la croissance de nombreuses plantes.
|
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La farine d'os est une teinture blanche et est également utile comme engrais pour accélérer la croissance de nombreuses plantes.
|
||||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=
|
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Cliquez avec le bouton droit sur un mouton pour blanchir sa laine. Cliquez avec le bouton droit sur une plante pour accélérer sa croissance. Cependant, toutes les plantes ne peuvent pas être fertilisées de cette manière. Lorsque vous cliquez avec le bouton droit sur un bloc d'herbe, les hautes herbes et les fleurs poussent autour.
|
||||||
Cliquez avec le bouton droit sur un mouton pour blanchir sa laine. Cliquez avec le bouton droit sur une plante pour accélérer sa croissance. Cependant, toutes les plantes ne peuvent pas être fertilisées de cette manière. Lorsque vous cliquez avec le bouton droit sur un bloc d'herbe, les hautes herbes et les fleurs poussent autour.
|
|
||||||
Speeds up plant growth=Accélère la croissance des plantes
|
Speeds up plant growth=Accélère la croissance des plantes
|
||||||
|
|
|
@ -169,6 +169,7 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
groups = {weapon=1,weapon_ranged=1,bow=1,cannot_block=1,enchantability=1},
|
groups = {weapon=1,weapon_ranged=1,bow=1,cannot_block=1,enchantability=1},
|
||||||
|
touch_interaction = "short_dig_long_place",
|
||||||
_mcl_uses = 385,
|
_mcl_uses = 385,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -235,6 +236,7 @@ for level=0, 2 do
|
||||||
on_place = function(itemstack)
|
on_place = function(itemstack)
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
|
touch_interaction = "short_dig_long_place",
|
||||||
_mcl_uses = 385,
|
_mcl_uses = 385,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -159,6 +159,7 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
groups = {weapon=1,weapon_ranged=1,crossbow=1,cannot_block=1,enchantability=1},
|
groups = {weapon=1,weapon_ranged=1,crossbow=1,cannot_block=1,enchantability=1},
|
||||||
|
touch_interaction = "short_dig_long_place",
|
||||||
_mcl_uses = 326,
|
_mcl_uses = 326,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -194,6 +195,7 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
groups = {weapon=1,weapon_ranged=1,crossbow=1,cannot_block=1,enchantability=1,not_in_creative_inventory=1},
|
groups = {weapon=1,weapon_ranged=1,crossbow=1,cannot_block=1,enchantability=1,not_in_creative_inventory=1},
|
||||||
|
touch_interaction = "short_dig_long_place",
|
||||||
_mcl_uses = 326,
|
_mcl_uses = 326,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -257,6 +259,7 @@ for level=0, 2 do
|
||||||
on_place = function(itemstack)
|
on_place = function(itemstack)
|
||||||
return itemstack
|
return itemstack
|
||||||
end,
|
end,
|
||||||
|
touch_interaction = "short_dig_long_place",
|
||||||
_mcl_uses = 385,
|
_mcl_uses = 385,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ minetest.register_tool("mcl_spyglass:spyglass",{
|
||||||
inventory_image = "mcl_spyglass.png",
|
inventory_image = "mcl_spyglass.png",
|
||||||
stack_max = 1,
|
stack_max = 1,
|
||||||
_mcl_toollike_wield = true,
|
_mcl_toollike_wield = true,
|
||||||
|
touch_interaction = "short_dig_long_place",
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
|
|
Loading…
Reference in New Issue