Split off general movement functions
parent
e7d9ded9ab
commit
cc77e109f5
|
@ -138,46 +138,6 @@ function mob_class:object_in_range(object)
|
||||||
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns true is node can deal damage to self
|
|
||||||
local is_node_dangerous = function(self, nodename)
|
|
||||||
local nn = nodename
|
|
||||||
if self.lava_damage > 0 then
|
|
||||||
if minetest.get_item_group(nn, "lava") ~= 0 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if self.fire_damage > 0 then
|
|
||||||
if minetest.get_item_group(nn, "fire") ~= 0 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Returns true if node is a water hazard
|
|
||||||
local is_node_waterhazard = function(self, nodename)
|
|
||||||
local nn = nodename
|
|
||||||
if self.water_damage > 0 then
|
|
||||||
if minetest.get_item_group(nn, "water") ~= 0 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].drowning and minetest.registered_nodes[nn].drowning > 0 then
|
|
||||||
if self.breath_max ~= -1 then
|
|
||||||
-- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case
|
|
||||||
-- this will prevent water-breathing mobs to classify water or e.g. sand below them as dangerous
|
|
||||||
if not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
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
|
||||||
|
@ -210,670 +170,6 @@ local node_ok = function(pos, fallback)
|
||||||
return minetest.registered_nodes[fallback]
|
return minetest.registered_nodes[fallback]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local can_jump_cliff = function(self)
|
|
||||||
local yaw = self.object:get_yaw()
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
|
|
||||||
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
|
||||||
local jump_c_multiplier = 1
|
|
||||||
if v2/self.walk_velocity/2>1 then
|
|
||||||
jump_c_multiplier = v2/self.walk_velocity/2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- where is front
|
|
||||||
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
|
||||||
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
|
||||||
|
|
||||||
--is there nothing under the block in front? if so jump the gap.
|
|
||||||
local nodLow = node_ok({
|
|
||||||
x = pos.x + dir_x-0.6,
|
|
||||||
y = pos.y - 0.5,
|
|
||||||
z = pos.z + dir_z-0.6
|
|
||||||
}, "air")
|
|
||||||
|
|
||||||
local nodFar = node_ok({
|
|
||||||
x = pos.x + dir_x*2,
|
|
||||||
y = pos.y - 0.5,
|
|
||||||
z = pos.z + dir_z*2
|
|
||||||
}, "air")
|
|
||||||
|
|
||||||
local nodFar2 = node_ok({
|
|
||||||
x = pos.x + dir_x*2.5,
|
|
||||||
y = pos.y - 0.5,
|
|
||||||
z = pos.z + dir_z*2.5
|
|
||||||
}, "air")
|
|
||||||
|
|
||||||
|
|
||||||
if minetest.registered_nodes[nodLow.name]
|
|
||||||
and minetest.registered_nodes[nodLow.name].walkable ~= true
|
|
||||||
|
|
||||||
|
|
||||||
and (minetest.registered_nodes[nodFar.name]
|
|
||||||
and minetest.registered_nodes[nodFar.name].walkable == true
|
|
||||||
|
|
||||||
or minetest.registered_nodes[nodFar2.name]
|
|
||||||
and minetest.registered_nodes[nodFar2.name].walkable == true)
|
|
||||||
|
|
||||||
then
|
|
||||||
--disable fear heigh while we make our jump
|
|
||||||
self._jumping_cliff = true
|
|
||||||
minetest.after(1, function()
|
|
||||||
if self and self.object then
|
|
||||||
self._jumping_cliff = false
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- is mob facing a cliff or danger
|
|
||||||
local is_at_cliff_or_danger = function(self)
|
|
||||||
|
|
||||||
if self.fear_height == 0 or can_jump_cliff(self) or self._jumping_cliff or not self.object:get_luaentity() then -- 0 for no falling protection!
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local yaw = self.object:get_yaw()
|
|
||||||
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)
|
|
||||||
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local ypos = pos.y + self.collisionbox[2] -- just above floor
|
|
||||||
|
|
||||||
local free_fall, blocker = minetest.line_of_sight(
|
|
||||||
{x = pos.x + dir_x, y = ypos, z = pos.z + dir_z},
|
|
||||||
{x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z})
|
|
||||||
if free_fall then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
local bnode = minetest.get_node(blocker)
|
|
||||||
local danger = is_node_dangerous(self, bnode.name)
|
|
||||||
if danger then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
local def = minetest.registered_nodes[bnode.name]
|
|
||||||
if def and def.walkable then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water
|
|
||||||
local is_at_water_danger = function(self)
|
|
||||||
|
|
||||||
|
|
||||||
if not self.object:get_luaentity() or can_jump_cliff(self) or self._jumping_cliff then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local yaw = self.object:get_yaw()
|
|
||||||
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)
|
|
||||||
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local ypos = pos.y + self.collisionbox[2] -- just above floor
|
|
||||||
|
|
||||||
local free_fall, blocker = minetest.line_of_sight(
|
|
||||||
{x = pos.x + dir_x, y = ypos, z = pos.z + dir_z},
|
|
||||||
{x = pos.x + dir_x, y = ypos - 3, z = pos.z + dir_z})
|
|
||||||
if free_fall then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
local bnode = minetest.get_node(blocker)
|
|
||||||
local waterdanger = is_node_waterhazard(self, bnode.name)
|
|
||||||
if
|
|
||||||
waterdanger and (is_node_waterhazard(self, self.standing_in) or is_node_waterhazard(self, self.standing_on)) then
|
|
||||||
return false
|
|
||||||
elseif waterdanger and (is_node_waterhazard(self, self.standing_in) or is_node_waterhazard(self, self.standing_on)) == false then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
local def = minetest.registered_nodes[bnode.name]
|
|
||||||
if def and def.walkable then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- jump if facing a solid node (not fences or gates)
|
|
||||||
local do_jump = function(self)
|
|
||||||
if not self.jump
|
|
||||||
or self.jump_height == 0
|
|
||||||
or self.fly
|
|
||||||
or (self.child and self.type ~= "monster")
|
|
||||||
or self.order == "stand" then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
self.facing_fence = false
|
|
||||||
|
|
||||||
-- something stopping us while moving?
|
|
||||||
if self.state ~= "stand"
|
|
||||||
and self:get_velocity() > 0.5
|
|
||||||
and self.object:get_velocity().y ~= 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local yaw = self.object:get_yaw()
|
|
||||||
|
|
||||||
-- what is mob standing on?
|
|
||||||
pos.y = pos.y + self.collisionbox[2] - 0.2
|
|
||||||
|
|
||||||
local nod = node_ok(pos)
|
|
||||||
|
|
||||||
if minetest.registered_nodes[nod.name].walkable == false then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
|
||||||
local jump_c_multiplier = 1
|
|
||||||
if v2/self.walk_velocity/2>1 then
|
|
||||||
jump_c_multiplier = v2/self.walk_velocity/2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- where is front
|
|
||||||
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
|
||||||
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
|
||||||
|
|
||||||
-- what is in front of mob?
|
|
||||||
nod = node_ok({
|
|
||||||
x = pos.x + dir_x,
|
|
||||||
y = pos.y + 0.5,
|
|
||||||
z = pos.z + dir_z
|
|
||||||
})
|
|
||||||
|
|
||||||
-- this is used to detect if there's a block on top of the block in front of the mob.
|
|
||||||
-- If there is, there is no point in jumping as we won't manage.
|
|
||||||
local nodTop = node_ok({
|
|
||||||
x = pos.x + dir_x,
|
|
||||||
y = pos.y + 1.5,
|
|
||||||
z = pos.z + dir_z
|
|
||||||
}, "air")
|
|
||||||
|
|
||||||
|
|
||||||
-- we don't attempt to jump if there's a stack of blocks blocking
|
|
||||||
if minetest.registered_nodes[nodTop.name].walkable == true and not (self.attack and self.state == "attack") then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- thin blocks that do not need to be jumped
|
|
||||||
if nod.name == node_snow then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local ndef = minetest.registered_nodes[nod.name]
|
|
||||||
if self.walk_chance == 0 or ndef and ndef.walkable or can_jump_cliff(self) then
|
|
||||||
|
|
||||||
if minetest.get_item_group(nod.name, "fence") == 0
|
|
||||||
and minetest.get_item_group(nod.name, "fence_gate") == 0
|
|
||||||
and minetest.get_item_group(nod.name, "wall") == 0 then
|
|
||||||
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
|
|
||||||
v.y = self.jump_height + 0.1 * 3
|
|
||||||
|
|
||||||
if can_jump_cliff(self) then
|
|
||||||
v=vector.multiply(v, vector.new(2.8,1,2.8))
|
|
||||||
end
|
|
||||||
|
|
||||||
self:set_animation( "jump") -- only when defined
|
|
||||||
|
|
||||||
self.object:set_velocity(v)
|
|
||||||
|
|
||||||
-- when in air move forward
|
|
||||||
minetest.after(0.3, function(self, v)
|
|
||||||
if (not self.object) or (not self.object:get_luaentity()) or (self.state == "die") then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self.object:set_acceleration({
|
|
||||||
x = v.x * 2,
|
|
||||||
y = DEFAULT_FALL_SPEED,
|
|
||||||
z = v.z * 2,
|
|
||||||
})
|
|
||||||
end, self, v)
|
|
||||||
|
|
||||||
if self.jump_sound_cooloff <= 0 then
|
|
||||||
self:mob_sound("jump")
|
|
||||||
self.jump_sound_cooloff = 0.5
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.facing_fence = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if we jumped against a block/wall 4 times then turn
|
|
||||||
if self.object:get_velocity().x ~= 0
|
|
||||||
and self.object:get_velocity().z ~= 0 then
|
|
||||||
|
|
||||||
self.jump_count = (self.jump_count or 0) + 1
|
|
||||||
|
|
||||||
if self.jump_count == 4 then
|
|
||||||
|
|
||||||
local yaw = self.object:get_yaw() or 0
|
|
||||||
|
|
||||||
yaw = self:set_yaw( yaw + 1.35, 8)
|
|
||||||
|
|
||||||
self.jump_count = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- should mob follow what I'm holding ?
|
|
||||||
local follow_holding = function(self, clicker)
|
|
||||||
if self.nofollow then return false end
|
|
||||||
|
|
||||||
if mcl_mobs.invis[clicker:get_player_name()] then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local item = clicker:get_wielded_item()
|
|
||||||
local t = type(self.follow)
|
|
||||||
|
|
||||||
-- single item
|
|
||||||
if t == "string"
|
|
||||||
and item:get_name() == self.follow then
|
|
||||||
return true
|
|
||||||
|
|
||||||
-- multiple items
|
|
||||||
elseif t == "table" then
|
|
||||||
|
|
||||||
for no = 1, #self.follow do
|
|
||||||
|
|
||||||
if self.follow[no] == item:get_name() then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- find and replace what mob is looking for (grass, wheat etc.)
|
|
||||||
local replace = function(self, pos)
|
|
||||||
|
|
||||||
if not self.replace_rate
|
|
||||||
or not self.replace_what
|
|
||||||
or self.child == true
|
|
||||||
or self.object:get_velocity().y ~= 0
|
|
||||||
or math.random(1, self.replace_rate) > 1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local what, with, y_offset
|
|
||||||
|
|
||||||
if type(self.replace_what[1]) == "table" then
|
|
||||||
|
|
||||||
local num = math.random(#self.replace_what)
|
|
||||||
|
|
||||||
what = self.replace_what[num][1] or ""
|
|
||||||
with = self.replace_what[num][2] or ""
|
|
||||||
y_offset = self.replace_what[num][3] or 0
|
|
||||||
else
|
|
||||||
what = self.replace_what
|
|
||||||
with = self.replace_with or ""
|
|
||||||
y_offset = self.replace_offset or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
pos.y = pos.y + y_offset
|
|
||||||
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
if node.name == what then
|
|
||||||
|
|
||||||
local oldnode = {name = what, param2 = node.param2}
|
|
||||||
local newnode = {name = with, param2 = node.param2}
|
|
||||||
local on_replace_return
|
|
||||||
|
|
||||||
if self.on_replace then
|
|
||||||
on_replace_return = self.on_replace(self, pos, oldnode, newnode)
|
|
||||||
end
|
|
||||||
|
|
||||||
if on_replace_return ~= false then
|
|
||||||
|
|
||||||
if mobs_griefing then
|
|
||||||
minetest.set_node(pos, newnode)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- check if daytime and also if mob is docile during daylight hours
|
|
||||||
local day_docile = function(self)
|
|
||||||
|
|
||||||
if self.docile_by_day == false then
|
|
||||||
|
|
||||||
return false
|
|
||||||
|
|
||||||
elseif self.docile_by_day == true
|
|
||||||
and self.time_of_day > 0.2
|
|
||||||
and self.time_of_day < 0.8 then
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- specific runaway
|
|
||||||
local specific_runaway = function(list, what)
|
|
||||||
|
|
||||||
-- no list so do not run
|
|
||||||
if list == nil then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- found entity on list to attack?
|
|
||||||
for no = 1, #list do
|
|
||||||
|
|
||||||
if list[no] == what then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- find someone to runaway from
|
|
||||||
local runaway_from = function(self)
|
|
||||||
|
|
||||||
if not self.runaway_from and self.state ~= "flop" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
local p, sp, dist
|
|
||||||
local player, obj, min_player
|
|
||||||
local type, name = "", ""
|
|
||||||
local min_dist = self.view_range + 1
|
|
||||||
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
|
||||||
|
|
||||||
for n = 1, #objs do
|
|
||||||
|
|
||||||
if objs[n]:is_player() then
|
|
||||||
|
|
||||||
if mcl_mobs.invis[ objs[n]:get_player_name() ]
|
|
||||||
or self.owner == objs[n]:get_player_name()
|
|
||||||
or (not self:object_in_range(objs[n])) then
|
|
||||||
type = ""
|
|
||||||
else
|
|
||||||
player = objs[n]
|
|
||||||
type = "player"
|
|
||||||
name = "player"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
obj = objs[n]:get_luaentity()
|
|
||||||
|
|
||||||
if obj then
|
|
||||||
player = obj.object
|
|
||||||
type = obj.type
|
|
||||||
name = obj.name or ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- find specific mob to runaway from
|
|
||||||
if name ~= "" and name ~= self.name
|
|
||||||
and specific_runaway(self.runaway_from, name) then
|
|
||||||
|
|
||||||
p = player:get_pos()
|
|
||||||
sp = s
|
|
||||||
|
|
||||||
-- aim higher to make looking up hills more realistic
|
|
||||||
p.y = p.y + 1
|
|
||||||
sp.y = sp.y + 1
|
|
||||||
|
|
||||||
dist = vector.distance(p, s)
|
|
||||||
|
|
||||||
|
|
||||||
-- choose closest player/mpb to runaway from
|
|
||||||
if dist < min_dist
|
|
||||||
and self:line_of_sight(sp, p, 2) == true then
|
|
||||||
min_dist = dist
|
|
||||||
min_player = player
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if min_player then
|
|
||||||
|
|
||||||
local lp = player:get_pos()
|
|
||||||
local vec = {
|
|
||||||
x = lp.x - s.x,
|
|
||||||
y = lp.y - s.y,
|
|
||||||
z = lp.z - s.z
|
|
||||||
}
|
|
||||||
|
|
||||||
local yaw = (atan(vec.z / vec.x) + 3 *math.pi/ 2) - self.rotate
|
|
||||||
|
|
||||||
if lp.x > s.x then
|
|
||||||
yaw = yaw + pi
|
|
||||||
end
|
|
||||||
|
|
||||||
yaw = self:set_yaw( yaw, 4)
|
|
||||||
self.state = "runaway"
|
|
||||||
self.runaway_timer = 3
|
|
||||||
self.following = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- follow player if owner or holding item, if fish outta water then flop
|
|
||||||
local follow_flop = function(self)
|
|
||||||
|
|
||||||
-- find player to follow
|
|
||||||
if (self.follow ~= ""
|
|
||||||
or self.order == "follow")
|
|
||||||
and not self.following
|
|
||||||
and self.state ~= "attack"
|
|
||||||
and self.order ~= "sit"
|
|
||||||
and self.state ~= "runaway" then
|
|
||||||
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
local players = minetest.get_connected_players()
|
|
||||||
|
|
||||||
for n = 1, #players do
|
|
||||||
|
|
||||||
if (self:object_in_range(players[n]))
|
|
||||||
and not mcl_mobs.invis[ players[n]:get_player_name() ] then
|
|
||||||
|
|
||||||
self.following = players[n]
|
|
||||||
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.type == "npc"
|
|
||||||
and self.order == "follow"
|
|
||||||
and self.state ~= "attack"
|
|
||||||
and self.order ~= "sit"
|
|
||||||
and self.owner ~= "" then
|
|
||||||
|
|
||||||
-- npc stop following player if not owner
|
|
||||||
if self.following
|
|
||||||
and self.owner
|
|
||||||
and self.owner ~= self.following:get_player_name() then
|
|
||||||
self.following = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- stop following player if not holding specific item,
|
|
||||||
-- mob is horny, fleeing or attacking
|
|
||||||
if self.following
|
|
||||||
and self.following:is_player()
|
|
||||||
and (follow_holding(self, self.following) == false or
|
|
||||||
self.horny or self.state == "runaway") then
|
|
||||||
self.following = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- follow that thing
|
|
||||||
if self.following then
|
|
||||||
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
local p
|
|
||||||
|
|
||||||
if self.following:is_player() then
|
|
||||||
|
|
||||||
p = self.following:get_pos()
|
|
||||||
|
|
||||||
elseif self.following.object then
|
|
||||||
|
|
||||||
p = self.following.object:get_pos()
|
|
||||||
end
|
|
||||||
|
|
||||||
if p then
|
|
||||||
|
|
||||||
local dist = vector.distance(p, s)
|
|
||||||
|
|
||||||
-- dont follow if out of range
|
|
||||||
if (not self:object_in_range(self.following)) then
|
|
||||||
self.following = nil
|
|
||||||
else
|
|
||||||
local vec = {
|
|
||||||
x = p.x - s.x,
|
|
||||||
z = p.z - s.z
|
|
||||||
}
|
|
||||||
|
|
||||||
local yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
|
||||||
|
|
||||||
if p.x > s.x then yaw = yaw +math.pi end
|
|
||||||
|
|
||||||
self:set_yaw( yaw, 2.35)
|
|
||||||
|
|
||||||
-- anyone but standing npc's can move along
|
|
||||||
if dist > 3
|
|
||||||
and self.order ~= "stand" then
|
|
||||||
|
|
||||||
self:set_velocity(self.follow_velocity)
|
|
||||||
|
|
||||||
if self.walk_chance ~= 0 then
|
|
||||||
self:set_animation( "run")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self:set_velocity(0)
|
|
||||||
self:set_animation( "stand")
|
|
||||||
end
|
|
||||||
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- swimmers flop when out of their element, and swim again when back in
|
|
||||||
if self.fly then
|
|
||||||
local s = self.object:get_pos()
|
|
||||||
if self:flight_check( s) == false then
|
|
||||||
|
|
||||||
self.state = "flop"
|
|
||||||
self.object:set_acceleration({x = 0, y = DEFAULT_FALL_SPEED, z = 0})
|
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
local sdef = minetest.registered_nodes[node_ok(vector.add(p, vector.new(0,self.collisionbox[2]-0.2,0))).name]
|
|
||||||
-- Flop on ground
|
|
||||||
if sdef and sdef.walkable then
|
|
||||||
if self.object:get_velocity().y < 0.1 then
|
|
||||||
self:mob_sound("flop")
|
|
||||||
self.object:set_velocity({
|
|
||||||
x = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
|
|
||||||
y = FLOP_HEIGHT,
|
|
||||||
z = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:set_animation( "stand", true)
|
|
||||||
|
|
||||||
return
|
|
||||||
elseif self.state == "flop" then
|
|
||||||
self.state = "stand"
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
self:set_velocity(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function go_to_pos(entity,b)
|
|
||||||
if not entity then return end
|
|
||||||
local s=entity.object:get_pos()
|
|
||||||
if not b then
|
|
||||||
--self.state = "stand"
|
|
||||||
return end
|
|
||||||
if vector.distance(b,s) < 1 then
|
|
||||||
--entity:set_velocity(0)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local v = { x = b.x - s.x, z = b.z - s.z }
|
|
||||||
local yaw = (atann(v.z / v.x) +math.pi/ 2) - entity.rotate
|
|
||||||
if b.x > s.x then yaw = yaw +math.pi end
|
|
||||||
entity.object:set_yaw(yaw)
|
|
||||||
entity:set_velocity(entity.follow_velocity)
|
|
||||||
entity:set_animation("walk")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function interact_with_door(self, action, target)
|
|
||||||
local p = self.object:get_pos()
|
|
||||||
--local t = minetest.get_timeofday()
|
|
||||||
--local dd = minetest.find_nodes_in_area(vector.offset(p,-1,-1,-1),vector.offset(p,1,1,1),{"group:door"})
|
|
||||||
--for _,d in pairs(dd) do
|
|
||||||
if target then
|
|
||||||
mcl_log("Door target is: ".. minetest.pos_to_string(target))
|
|
||||||
|
|
||||||
local n = minetest.get_node(target)
|
|
||||||
if n.name:find("_b_") or n.name:find("_t_") then
|
|
||||||
mcl_log("Door")
|
|
||||||
local def = minetest.registered_nodes[n.name]
|
|
||||||
local closed = n.name:find("_b_1") or n.name:find("_t_1")
|
|
||||||
--if self.state == PATHFINDING then
|
|
||||||
if closed and action == "open" and def.on_rightclick then
|
|
||||||
mcl_log("Open door")
|
|
||||||
def.on_rightclick(target,n,self)
|
|
||||||
end
|
|
||||||
if not closed and action == "close" and def.on_rightclick then
|
|
||||||
mcl_log("Close door")
|
|
||||||
def.on_rightclick(target,n,self)
|
|
||||||
end
|
|
||||||
--else
|
|
||||||
else
|
|
||||||
mcl_log("Not door")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
mcl_log("no target. cannot try and open or close door")
|
|
||||||
end
|
|
||||||
--end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function do_pathfind_action (self, action)
|
|
||||||
if action then
|
|
||||||
mcl_log("Action present")
|
|
||||||
local type = action["type"]
|
|
||||||
local action_val = action["action"]
|
|
||||||
local target = action["target"]
|
|
||||||
if target then
|
|
||||||
mcl_log("Target: ".. minetest.pos_to_string(target))
|
|
||||||
end
|
|
||||||
if type and type == "door" then
|
|
||||||
mcl_log("Type is door")
|
|
||||||
interact_with_door(self, action_val, target)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- execute current state (stand, walk, run, attacks)
|
-- execute current state (stand, walk, run, attacks)
|
||||||
-- returns true if mob has died
|
-- returns true if mob has died
|
||||||
local do_states = function(self, dtime)
|
local do_states = function(self, dtime)
|
||||||
|
@ -926,7 +222,7 @@ local do_states = function(self, dtime)
|
||||||
if self.walk_chance ~= 0
|
if self.walk_chance ~= 0
|
||||||
and self.facing_fence ~= true
|
and self.facing_fence ~= true
|
||||||
and math.random(1, 100) <= self.walk_chance
|
and math.random(1, 100) <= self.walk_chance
|
||||||
and is_at_cliff_or_danger(self) == false then
|
and self:is_at_cliff_or_danger() == false then
|
||||||
|
|
||||||
self:set_velocity(self.walk_velocity)
|
self:set_velocity(self.walk_velocity)
|
||||||
self.state = "walk"
|
self.state = "walk"
|
||||||
|
@ -965,8 +261,8 @@ local do_states = function(self, dtime)
|
||||||
local is_in_danger = false
|
local is_in_danger = false
|
||||||
if lp then
|
if lp then
|
||||||
-- If mob in or on dangerous block, look for land
|
-- If mob in or on dangerous block, look for land
|
||||||
if (is_node_dangerous(self, self.standing_in) or
|
if (self:is_node_dangerous(self.standing_in) or
|
||||||
is_node_dangerous(self, self.standing_on)) or (is_node_waterhazard(self, self.standing_in) or is_node_waterhazard(self, self.standing_on)) and (not self.fly) then
|
self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then
|
||||||
is_in_danger = true
|
is_in_danger = true
|
||||||
|
|
||||||
-- If mob in or on dangerous block, look for land
|
-- If mob in or on dangerous block, look for land
|
||||||
|
@ -1020,7 +316,7 @@ local do_states = function(self, dtime)
|
||||||
-- stand for great fall or danger or fence in front
|
-- stand for great fall or danger or fence in front
|
||||||
local cliff_or_danger = false
|
local cliff_or_danger = false
|
||||||
if is_in_danger then
|
if is_in_danger then
|
||||||
cliff_or_danger = is_at_cliff_or_danger(self)
|
cliff_or_danger = self:is_at_cliff_or_danger()
|
||||||
end
|
end
|
||||||
if self.facing_fence == true
|
if self.facing_fence == true
|
||||||
or cliff_or_danger
|
or cliff_or_danger
|
||||||
|
@ -1052,7 +348,7 @@ local do_states = function(self, dtime)
|
||||||
|
|
||||||
-- stop after 5 seconds or when at cliff
|
-- stop after 5 seconds or when at cliff
|
||||||
if self.runaway_timer > 5
|
if self.runaway_timer > 5
|
||||||
or is_at_cliff_or_danger(self) then
|
or self:is_at_cliff_or_danger() then
|
||||||
self.runaway_timer = 0
|
self.runaway_timer = 0
|
||||||
self:set_velocity(0)
|
self:set_velocity(0)
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
|
@ -1285,7 +581,7 @@ local do_states = function(self, dtime)
|
||||||
self:smart_mobs(s, p, dist, dtime)
|
self:smart_mobs(s, p, dist, dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_at_cliff_or_danger(self) 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")
|
||||||
|
@ -1486,15 +782,6 @@ local function check_herd(self,dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local teleport = function(self, target)
|
|
||||||
if self.do_teleport then
|
|
||||||
if self.do_teleport(self, target) == false then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- deal damage and effects when mob punched
|
-- deal damage and effects when mob punched
|
||||||
local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
||||||
|
|
||||||
|
@ -2014,24 +1301,10 @@ local mob_activate = function(self, staticdata, def, dtime)
|
||||||
|
|
||||||
-- run after_activate
|
-- run after_activate
|
||||||
if def.after_activate then
|
if def.after_activate then
|
||||||
|
|
||||||
def.after_activate(self, staticdata, def, dtime)
|
def.after_activate(self, staticdata, def, dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_aggro(self,dtime)
|
|
||||||
if not self._aggro or not self.attack then return end
|
|
||||||
if not self._check_aggro_timer or self._check_aggro_timer > 5 then
|
|
||||||
self._check_aggro_timer = 0
|
|
||||||
if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
|
||||||
self._aggro = nil
|
|
||||||
self.attack = nil
|
|
||||||
self.state = "stand"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self._check_aggro_timer = self._check_aggro_timer + dtime
|
|
||||||
end
|
|
||||||
|
|
||||||
-- main mob function
|
-- main mob function
|
||||||
local mob_step = function(self, dtime)
|
local mob_step = function(self, dtime)
|
||||||
self.lifetimer = self.lifetimer - dtime
|
self.lifetimer = self.lifetimer - dtime
|
||||||
|
@ -2088,7 +1361,7 @@ local mob_step = function(self, dtime)
|
||||||
self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d})
|
self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d})
|
||||||
end
|
end
|
||||||
|
|
||||||
check_aggro(self,dtime)
|
self:check_aggro(dtime)
|
||||||
self:check_item_pickup()
|
self:check_item_pickup()
|
||||||
|
|
||||||
self:check_particlespawners(dtime)
|
self:check_particlespawners(dtime)
|
||||||
|
@ -2120,7 +1393,7 @@ local mob_step = function(self, dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
--Mob following code.
|
--Mob following code.
|
||||||
follow_flop(self)
|
self:follow_flop()
|
||||||
|
|
||||||
--set animation speed relitive to velocity
|
--set animation speed relitive to velocity
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
|
@ -2314,13 +1587,11 @@ local mob_step = function(self, dtime)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- node replace check (cow eats grass etc.)
|
-- node replace check (cow eats grass etc.)
|
||||||
replace(self, pos)
|
self:replace(pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:monster_attack()
|
self:monster_attack()
|
||||||
|
|
||||||
self:npc_attack()
|
self:npc_attack()
|
||||||
|
|
||||||
self:check_breeding()
|
self:check_breeding()
|
||||||
|
|
||||||
if do_states(self, dtime) then
|
if do_states(self, dtime) then
|
||||||
|
@ -2331,13 +1602,13 @@ local mob_step = function(self, dtime)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
do_jump(self)
|
self:do_jump()
|
||||||
|
|
||||||
self:set_armor_texture()
|
self:set_armor_texture()
|
||||||
|
|
||||||
runaway_from(self)
|
self:check_runaway_from()
|
||||||
|
|
||||||
if is_at_water_danger(self) and self.state ~= "attack" then
|
if self:is_at_water_danger() and self.state ~= "attack" then
|
||||||
if math.random(1, 10) <= 6 then
|
if math.random(1, 10) <= 6 then
|
||||||
self:set_velocity(0)
|
self:set_velocity(0)
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
|
@ -2389,7 +1660,7 @@ local mob_step = function(self, dtime)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_at_cliff_or_danger(self) then
|
if self:is_at_cliff_or_danger() then
|
||||||
self:set_velocity(0)
|
self:set_velocity(0)
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
self:set_animation( "stand")
|
self:set_animation( "stand")
|
||||||
|
@ -2400,9 +1671,8 @@ end
|
||||||
|
|
||||||
|
|
||||||
-- default function when mobs are blown up with TNT
|
-- default function when mobs are blown up with TNT
|
||||||
local do_tnt = function(obj, damage)
|
local function do_tnt(damage)
|
||||||
|
self.object:punch(self.object, 1.0, {
|
||||||
obj.object:punch(obj.object, 1.0, {
|
|
||||||
full_punch_interval = 1.0,
|
full_punch_interval = 1.0,
|
||||||
damage_groups = {fleshy = damage},
|
damage_groups = {fleshy = damage},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
@ -2449,6 +1719,9 @@ local create_mob_on_rightclick = function(on_rightclick)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--#### REGISTER FUNCS
|
||||||
-- register mob entity
|
-- register mob entity
|
||||||
function mcl_mobs.register_mob(name, def)
|
function mcl_mobs.register_mob(name, def)
|
||||||
|
|
||||||
|
@ -2480,7 +1753,6 @@ if collisionbox[5] < 0.79 then
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_entity(name, setmetatable({
|
minetest.register_entity(name, setmetatable({
|
||||||
|
|
||||||
use_texture_alpha = def.use_texture_alpha,
|
use_texture_alpha = def.use_texture_alpha,
|
||||||
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 = def.head_yaw_offset or 0, -- for wonkey model bones
|
head_yaw_offset = def.head_yaw_offset or 0, -- for wonkey model bones
|
||||||
|
@ -2604,7 +1876,6 @@ minetest.register_entity(name, setmetatable({
|
||||||
shooter_avoid_enemy = def.shooter_avoid_enemy,
|
shooter_avoid_enemy = def.shooter_avoid_enemy,
|
||||||
strafes = def.strafes,
|
strafes = def.strafes,
|
||||||
avoid_distance = def.avoid_distance or 9,
|
avoid_distance = def.avoid_distance or 9,
|
||||||
teleport = teleport,
|
|
||||||
do_teleport = def.do_teleport,
|
do_teleport = def.do_teleport,
|
||||||
spawn_class = def.spawn_class,
|
spawn_class = def.spawn_class,
|
||||||
can_spawn = def.can_spawn,
|
can_spawn = def.can_spawn,
|
||||||
|
|
|
@ -6,6 +6,17 @@ local enable_pathfinding = true
|
||||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||||
local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
||||||
|
|
||||||
|
-- check if daytime and also if mob is docile during daylight hours
|
||||||
|
function mob_class:day_docile()
|
||||||
|
if self.docile_by_day == false then
|
||||||
|
return false
|
||||||
|
elseif self.docile_by_day == true
|
||||||
|
and self.time_of_day > 0.2
|
||||||
|
and self.time_of_day < 0.8 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- attack player/mob
|
-- attack player/mob
|
||||||
function mob_class:do_attack(player)
|
function mob_class:do_attack(player)
|
||||||
|
|
||||||
|
@ -307,7 +318,7 @@ function mob_class:monster_attack()
|
||||||
if not damage_enabled
|
if not damage_enabled
|
||||||
or self.passive ~= false
|
or self.passive ~= false
|
||||||
or self.state == "attack"
|
or self.state == "attack"
|
||||||
or day_docile(self) then
|
or self:day_docile() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -490,3 +501,17 @@ function mob_class:boom(pos, strength, fire)
|
||||||
-- delete the object after it punched the player to avoid nil entities in e.g. mcl_shields!!
|
-- delete the object after it punched the player to avoid nil entities in e.g. mcl_shields!!
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function mob_class:check_aggro(dtime)
|
||||||
|
if not self._aggro or not self.attack then return end
|
||||||
|
if not self._check_aggro_timer or self._check_aggro_timer > 5 then
|
||||||
|
self._check_aggro_timer = 0
|
||||||
|
if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
||||||
|
self._aggro = nil
|
||||||
|
self.attack = nil
|
||||||
|
self.state = "stand"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self._check_aggro_timer = self._check_aggro_timer + dtime
|
||||||
|
end
|
||||||
|
|
|
@ -5,14 +5,22 @@ mcl_mobs.mob_class_meta = {__index = mcl_mobs.mob_class}
|
||||||
local path = minetest.get_modpath(minetest.get_current_modname())
|
local path = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
|
||||||
--api and helpers
|
--api and helpers
|
||||||
|
-- effects: sounds and particles mostly
|
||||||
dofile(path .. "/effects.lua")
|
dofile(path .. "/effects.lua")
|
||||||
|
-- physics: involuntary mob movement - particularly falling and death
|
||||||
dofile(path .. "/physics.lua")
|
dofile(path .. "/physics.lua")
|
||||||
|
-- movement: general voluntary mob movement, walking avoiding cliffs etc.
|
||||||
dofile(path .. "/movement.lua")
|
dofile(path .. "/movement.lua")
|
||||||
|
-- items: item management for mobs
|
||||||
dofile(path .. "/items.lua")
|
dofile(path .. "/items.lua")
|
||||||
|
-- pathfinding: pathfinding to target positions
|
||||||
dofile(path .. "/pathfinding.lua")
|
dofile(path .. "/pathfinding.lua")
|
||||||
|
-- combat: attack logic
|
||||||
dofile(path .. "/combat.lua")
|
dofile(path .. "/combat.lua")
|
||||||
|
-- the enity 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")
|
||||||
|
|
|
@ -1,5 +1,58 @@
|
||||||
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 DEFAULT_FALL_SPEED = -9.81*1.5
|
||||||
|
local FLOP_HEIGHT = 6
|
||||||
|
local FLOP_HOR_SPEED = 1.5
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
-- Returns true is node can deal damage to self
|
||||||
|
function mob_class:is_node_dangerous(nodename)
|
||||||
|
local nn = nodename
|
||||||
|
if self.lava_damage > 0 then
|
||||||
|
if minetest.get_item_group(nn, "lava") ~= 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.fire_damage > 0 then
|
||||||
|
if minetest.get_item_group(nn, "fire") ~= 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Returns true if node is a water hazard
|
||||||
|
function mob_class:is_node_waterhazard(nodename)
|
||||||
|
local nn = nodename
|
||||||
|
if self.water_damage > 0 then
|
||||||
|
if minetest.get_item_group(nn, "water") ~= 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].drowning and minetest.registered_nodes[nn].drowning > 0 then
|
||||||
|
if self.breath_max ~= -1 then
|
||||||
|
-- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case
|
||||||
|
-- this will prevent water-breathing mobs to classify water or e.g. sand below them as dangerous
|
||||||
|
if not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- check line of sight (BrunoMine)
|
-- check line of sight (BrunoMine)
|
||||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||||
|
@ -69,3 +122,655 @@ function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mob_class:can_jump_cliff()
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
|
||||||
|
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
||||||
|
local jump_c_multiplier = 1
|
||||||
|
if v2/self.walk_velocity/2>1 then
|
||||||
|
jump_c_multiplier = v2/self.walk_velocity/2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- where is front
|
||||||
|
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||||
|
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||||
|
|
||||||
|
--is there nothing under the block in front? if so jump the gap.
|
||||||
|
local nodLow = node_ok({
|
||||||
|
x = pos.x + dir_x-0.6,
|
||||||
|
y = pos.y - 0.5,
|
||||||
|
z = pos.z + dir_z-0.6
|
||||||
|
}, "air")
|
||||||
|
|
||||||
|
local nodFar = node_ok({
|
||||||
|
x = pos.x + dir_x*2,
|
||||||
|
y = pos.y - 0.5,
|
||||||
|
z = pos.z + dir_z*2
|
||||||
|
}, "air")
|
||||||
|
|
||||||
|
local nodFar2 = node_ok({
|
||||||
|
x = pos.x + dir_x*2.5,
|
||||||
|
y = pos.y - 0.5,
|
||||||
|
z = pos.z + dir_z*2.5
|
||||||
|
}, "air")
|
||||||
|
|
||||||
|
|
||||||
|
if minetest.registered_nodes[nodLow.name]
|
||||||
|
and minetest.registered_nodes[nodLow.name].walkable ~= true
|
||||||
|
|
||||||
|
|
||||||
|
and (minetest.registered_nodes[nodFar.name]
|
||||||
|
and minetest.registered_nodes[nodFar.name].walkable == true
|
||||||
|
|
||||||
|
or minetest.registered_nodes[nodFar2.name]
|
||||||
|
and minetest.registered_nodes[nodFar2.name].walkable == true)
|
||||||
|
|
||||||
|
then
|
||||||
|
--disable fear heigh while we make our jump
|
||||||
|
self._jumping_cliff = true
|
||||||
|
minetest.after(1, function()
|
||||||
|
if self and self.object then
|
||||||
|
self._jumping_cliff = false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- is mob facing a cliff or danger
|
||||||
|
function mob_class:is_at_cliff_or_danger()
|
||||||
|
if self.fear_height == 0 or self:can_jump_cliff() or self._jumping_cliff or not self.object:get_luaentity() then -- 0 for no falling protection!
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)
|
||||||
|
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local ypos = pos.y + self.collisionbox[2] -- just above floor
|
||||||
|
|
||||||
|
local free_fall, blocker = minetest.line_of_sight(
|
||||||
|
{x = pos.x + dir_x, y = ypos, z = pos.z + dir_z},
|
||||||
|
{x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z})
|
||||||
|
if free_fall then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
local bnode = minetest.get_node(blocker)
|
||||||
|
local danger = self:is_node_dangerous(bnode.name)
|
||||||
|
if danger then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
local def = minetest.registered_nodes[bnode.name]
|
||||||
|
if def and def.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water
|
||||||
|
function mob_class:is_at_water_danger()
|
||||||
|
if not self.object:get_luaentity() or self:can_jump_cliff() or self._jumping_cliff then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)
|
||||||
|
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local ypos = pos.y + self.collisionbox[2] -- just above floor
|
||||||
|
|
||||||
|
local free_fall, blocker = minetest.line_of_sight(
|
||||||
|
{x = pos.x + dir_x, y = ypos, z = pos.z + dir_z},
|
||||||
|
{x = pos.x + dir_x, y = ypos - 3, z = pos.z + dir_z})
|
||||||
|
if free_fall then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
local bnode = minetest.get_node(blocker)
|
||||||
|
local waterdanger = self:is_node_waterhazard(bnode.name)
|
||||||
|
if
|
||||||
|
waterdanger and (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard( self.standing_on)) then
|
||||||
|
return false
|
||||||
|
elseif waterdanger and (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) == false then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
local def = minetest.registered_nodes[bnode.name]
|
||||||
|
if def and def.walkable then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- jump if facing a solid node (not fences or gates)
|
||||||
|
function mob_class:do_jump()
|
||||||
|
if not self.jump
|
||||||
|
or self.jump_height == 0
|
||||||
|
or self.fly
|
||||||
|
or (self.child and self.type ~= "monster")
|
||||||
|
or self.order == "stand" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
self.facing_fence = false
|
||||||
|
|
||||||
|
-- something stopping us while moving?
|
||||||
|
if self.state ~= "stand"
|
||||||
|
and self:get_velocity() > 0.5
|
||||||
|
and self.object:get_velocity().y ~= 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local yaw = self.object:get_yaw()
|
||||||
|
|
||||||
|
-- what is mob standing on?
|
||||||
|
pos.y = pos.y + self.collisionbox[2] - 0.2
|
||||||
|
|
||||||
|
local nod = node_ok(pos)
|
||||||
|
|
||||||
|
if minetest.registered_nodes[nod.name].walkable == false then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
||||||
|
local jump_c_multiplier = 1
|
||||||
|
if v2/self.walk_velocity/2>1 then
|
||||||
|
jump_c_multiplier = v2/self.walk_velocity/2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- where is front
|
||||||
|
local dir_x = -math.sin(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||||
|
local dir_z = math.cos(yaw) * (self.collisionbox[4] + 0.5)*jump_c_multiplier+0.6
|
||||||
|
|
||||||
|
-- what is in front of mob?
|
||||||
|
nod = node_ok({
|
||||||
|
x = pos.x + dir_x,
|
||||||
|
y = pos.y + 0.5,
|
||||||
|
z = pos.z + dir_z
|
||||||
|
})
|
||||||
|
|
||||||
|
-- this is used to detect if there's a block on top of the block in front of the mob.
|
||||||
|
-- If there is, there is no point in jumping as we won't manage.
|
||||||
|
local nodTop = node_ok({
|
||||||
|
x = pos.x + dir_x,
|
||||||
|
y = pos.y + 1.5,
|
||||||
|
z = pos.z + dir_z
|
||||||
|
}, "air")
|
||||||
|
|
||||||
|
|
||||||
|
-- we don't attempt to jump if there's a stack of blocks blocking
|
||||||
|
if minetest.registered_nodes[nodTop.name].walkable == true and not (self.attack and self.state == "attack") then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- thin blocks that do not need to be jumped
|
||||||
|
if nod.name == node_snow then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local ndef = minetest.registered_nodes[nod.name]
|
||||||
|
if self.walk_chance == 0 or ndef and ndef.walkable or self:can_jump_cliff() then
|
||||||
|
|
||||||
|
if minetest.get_item_group(nod.name, "fence") == 0
|
||||||
|
and minetest.get_item_group(nod.name, "fence_gate") == 0
|
||||||
|
and minetest.get_item_group(nod.name, "wall") == 0 then
|
||||||
|
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
|
||||||
|
v.y = self.jump_height + 0.1 * 3
|
||||||
|
|
||||||
|
if self:can_jump_cliff() then
|
||||||
|
v=vector.multiply(v, vector.new(2.8,1,2.8))
|
||||||
|
end
|
||||||
|
|
||||||
|
self:set_animation( "jump") -- only when defined
|
||||||
|
|
||||||
|
self.object:set_velocity(v)
|
||||||
|
|
||||||
|
-- when in air move forward
|
||||||
|
minetest.after(0.3, function(self, v)
|
||||||
|
if (not self.object) or (not self.object:get_luaentity()) or (self.state == "die") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.object:set_acceleration({
|
||||||
|
x = v.x * 2,
|
||||||
|
y = DEFAULT_FALL_SPEED,
|
||||||
|
z = v.z * 2,
|
||||||
|
})
|
||||||
|
end, self, v)
|
||||||
|
|
||||||
|
if self.jump_sound_cooloff <= 0 then
|
||||||
|
self:mob_sound("jump")
|
||||||
|
self.jump_sound_cooloff = 0.5
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.facing_fence = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we jumped against a block/wall 4 times then turn
|
||||||
|
if self.object:get_velocity().x ~= 0
|
||||||
|
and self.object:get_velocity().z ~= 0 then
|
||||||
|
|
||||||
|
self.jump_count = (self.jump_count or 0) + 1
|
||||||
|
|
||||||
|
if self.jump_count == 4 then
|
||||||
|
|
||||||
|
local yaw = self.object:get_yaw() or 0
|
||||||
|
|
||||||
|
yaw = self:set_yaw( yaw + 1.35, 8)
|
||||||
|
|
||||||
|
self.jump_count = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- should mob follow what I'm holding ?
|
||||||
|
function mob_class:follow_holding(clicker)
|
||||||
|
if self.nofollow then return false end
|
||||||
|
|
||||||
|
if mcl_mobs.invis[clicker:get_player_name()] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local item = clicker:get_wielded_item()
|
||||||
|
local t = type(self.follow)
|
||||||
|
|
||||||
|
-- single item
|
||||||
|
if t == "string"
|
||||||
|
and item:get_name() == self.follow then
|
||||||
|
return true
|
||||||
|
|
||||||
|
-- multiple items
|
||||||
|
elseif t == "table" then
|
||||||
|
|
||||||
|
for no = 1, #self.follow do
|
||||||
|
|
||||||
|
if self.follow[no] == item:get_name() then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- find and replace what mob is looking for (grass, wheat etc.)
|
||||||
|
function mob_class:replace(pos)
|
||||||
|
|
||||||
|
if not self.replace_rate
|
||||||
|
or not self.replace_what
|
||||||
|
or self.child == true
|
||||||
|
or self.object:get_velocity().y ~= 0
|
||||||
|
or math.random(1, self.replace_rate) > 1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local what, with, y_offset
|
||||||
|
|
||||||
|
if type(self.replace_what[1]) == "table" then
|
||||||
|
|
||||||
|
local num = math.random(#self.replace_what)
|
||||||
|
|
||||||
|
what = self.replace_what[num][1] or ""
|
||||||
|
with = self.replace_what[num][2] or ""
|
||||||
|
y_offset = self.replace_what[num][3] or 0
|
||||||
|
else
|
||||||
|
what = self.replace_what
|
||||||
|
with = self.replace_with or ""
|
||||||
|
y_offset = self.replace_offset or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
pos.y = pos.y + y_offset
|
||||||
|
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
if node.name == what then
|
||||||
|
|
||||||
|
local oldnode = {name = what, param2 = node.param2}
|
||||||
|
local newnode = {name = with, param2 = node.param2}
|
||||||
|
local on_replace_return
|
||||||
|
|
||||||
|
if self.on_replace then
|
||||||
|
on_replace_return = self.on_replace(self, pos, oldnode, newnode)
|
||||||
|
end
|
||||||
|
|
||||||
|
if on_replace_return ~= false then
|
||||||
|
|
||||||
|
if mobs_griefing then
|
||||||
|
minetest.set_node(pos, newnode)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- specific runaway
|
||||||
|
local specific_runaway = function(list, what)
|
||||||
|
if type(list) ~= "table" then
|
||||||
|
list = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- no list so do not run
|
||||||
|
if list == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- found entity on list to attack?
|
||||||
|
for no = 1, #list do
|
||||||
|
|
||||||
|
if list[no] == what then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- find someone to runaway from
|
||||||
|
function mob_class:check_runaway_from()
|
||||||
|
if not self.runaway_from and self.state ~= "flop" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local s = self.object:get_pos()
|
||||||
|
local p, sp, dist
|
||||||
|
local player, obj, min_player
|
||||||
|
local type, name = "", ""
|
||||||
|
local min_dist = self.view_range + 1
|
||||||
|
local objs = minetest.get_objects_inside_radius(s, self.view_range)
|
||||||
|
|
||||||
|
for n = 1, #objs do
|
||||||
|
|
||||||
|
if objs[n]:is_player() then
|
||||||
|
|
||||||
|
if mcl_mobs.invis[ objs[n]:get_player_name() ]
|
||||||
|
or self.owner == objs[n]:get_player_name()
|
||||||
|
or (not self:object_in_range(objs[n])) then
|
||||||
|
type = ""
|
||||||
|
else
|
||||||
|
player = objs[n]
|
||||||
|
type = "player"
|
||||||
|
name = "player"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj = objs[n]:get_luaentity()
|
||||||
|
|
||||||
|
if obj then
|
||||||
|
player = obj.object
|
||||||
|
type = obj.type
|
||||||
|
name = obj.name or ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find specific mob to runaway from
|
||||||
|
if name ~= "" and name ~= self.name
|
||||||
|
and specific_runaway(self.runaway_from, name) then
|
||||||
|
|
||||||
|
p = player:get_pos()
|
||||||
|
sp = s
|
||||||
|
|
||||||
|
-- aim higher to make looking up hills more realistic
|
||||||
|
p.y = p.y + 1
|
||||||
|
sp.y = sp.y + 1
|
||||||
|
|
||||||
|
dist = vector.distance(p, s)
|
||||||
|
|
||||||
|
|
||||||
|
-- choose closest player/mpb to runaway from
|
||||||
|
if dist < min_dist
|
||||||
|
and self:line_of_sight(sp, p, 2) == true then
|
||||||
|
min_dist = dist
|
||||||
|
min_player = player
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if min_player then
|
||||||
|
|
||||||
|
local lp = player:get_pos()
|
||||||
|
local vec = {
|
||||||
|
x = lp.x - s.x,
|
||||||
|
y = lp.y - s.y,
|
||||||
|
z = lp.z - s.z
|
||||||
|
}
|
||||||
|
|
||||||
|
local yaw = (atan(vec.z / vec.x) + 3 *math.pi/ 2) - self.rotate
|
||||||
|
|
||||||
|
if lp.x > s.x then
|
||||||
|
yaw = yaw + pi
|
||||||
|
end
|
||||||
|
|
||||||
|
yaw = self:set_yaw( yaw, 4)
|
||||||
|
self.state = "runaway"
|
||||||
|
self.runaway_timer = 3
|
||||||
|
self.following = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- follow player if owner or holding item, if fish outta water then flop
|
||||||
|
function mob_class:follow_flop()
|
||||||
|
|
||||||
|
-- find player to follow
|
||||||
|
if (self.follow ~= ""
|
||||||
|
or self.order == "follow")
|
||||||
|
and not self.following
|
||||||
|
and self.state ~= "attack"
|
||||||
|
and self.order ~= "sit"
|
||||||
|
and self.state ~= "runaway" then
|
||||||
|
|
||||||
|
local s = self.object:get_pos()
|
||||||
|
local players = minetest.get_connected_players()
|
||||||
|
|
||||||
|
for n = 1, #players do
|
||||||
|
|
||||||
|
if (self:object_in_range(players[n]))
|
||||||
|
and not mcl_mobs.invis[ players[n]:get_player_name() ] then
|
||||||
|
|
||||||
|
self.following = players[n]
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.type == "npc"
|
||||||
|
and self.order == "follow"
|
||||||
|
and self.state ~= "attack"
|
||||||
|
and self.order ~= "sit"
|
||||||
|
and self.owner ~= "" then
|
||||||
|
|
||||||
|
-- npc stop following player if not owner
|
||||||
|
if self.following
|
||||||
|
and self.owner
|
||||||
|
and self.owner ~= self.following:get_player_name() then
|
||||||
|
self.following = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- stop following player if not holding specific item,
|
||||||
|
-- mob is horny, fleeing or attacking
|
||||||
|
if self.following
|
||||||
|
and self.following:is_player()
|
||||||
|
and (self:follow_holding(self.following) == false or
|
||||||
|
self.horny or self.state == "runaway") then
|
||||||
|
self.following = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- follow that thing
|
||||||
|
if self.following then
|
||||||
|
|
||||||
|
local s = self.object:get_pos()
|
||||||
|
local p
|
||||||
|
|
||||||
|
if self.following:is_player() then
|
||||||
|
|
||||||
|
p = self.following:get_pos()
|
||||||
|
|
||||||
|
elseif self.following.object then
|
||||||
|
|
||||||
|
p = self.following.object:get_pos()
|
||||||
|
end
|
||||||
|
|
||||||
|
if p then
|
||||||
|
|
||||||
|
local dist = vector.distance(p, s)
|
||||||
|
|
||||||
|
-- dont follow if out of range
|
||||||
|
if (not self:object_in_range(self.following)) then
|
||||||
|
self.following = nil
|
||||||
|
else
|
||||||
|
local vec = {
|
||||||
|
x = p.x - s.x,
|
||||||
|
z = p.z - s.z
|
||||||
|
}
|
||||||
|
|
||||||
|
local yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
||||||
|
|
||||||
|
if p.x > s.x then yaw = yaw +math.pi end
|
||||||
|
|
||||||
|
self:set_yaw( yaw, 2.35)
|
||||||
|
|
||||||
|
-- anyone but standing npc's can move along
|
||||||
|
if dist > 3
|
||||||
|
and self.order ~= "stand" then
|
||||||
|
|
||||||
|
self:set_velocity(self.follow_velocity)
|
||||||
|
|
||||||
|
if self.walk_chance ~= 0 then
|
||||||
|
self:set_animation( "run")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:set_velocity(0)
|
||||||
|
self:set_animation( "stand")
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- swimmers flop when out of their element, and swim again when back in
|
||||||
|
if self.fly then
|
||||||
|
local s = self.object:get_pos()
|
||||||
|
if self:flight_check( s) == false then
|
||||||
|
|
||||||
|
self.state = "flop"
|
||||||
|
self.object:set_acceleration({x = 0, y = DEFAULT_FALL_SPEED, z = 0})
|
||||||
|
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
local sdef = minetest.registered_nodes[node_ok(vector.add(p, vector.new(0,self.collisionbox[2]-0.2,0))).name]
|
||||||
|
-- Flop on ground
|
||||||
|
if sdef and sdef.walkable then
|
||||||
|
if self.object:get_velocity().y < 0.1 then
|
||||||
|
self:mob_sound("flop")
|
||||||
|
self.object:set_velocity({
|
||||||
|
x = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
|
||||||
|
y = FLOP_HEIGHT,
|
||||||
|
z = math.random(-FLOP_HOR_SPEED, FLOP_HOR_SPEED),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:set_animation( "stand", true)
|
||||||
|
|
||||||
|
return
|
||||||
|
elseif self.state == "flop" then
|
||||||
|
self.state = "stand"
|
||||||
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
|
self:set_velocity(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:go_to_pos(b)
|
||||||
|
if not self then return end
|
||||||
|
local s=self.object:get_pos()
|
||||||
|
if not b then
|
||||||
|
--self.state = "stand"
|
||||||
|
return end
|
||||||
|
if vector.distance(b,s) < 1 then
|
||||||
|
--self:set_velocity(0)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
local v = { x = b.x - s.x, z = b.z - s.z }
|
||||||
|
local yaw = (atann(v.z / v.x) +math.pi/ 2) - self.rotate
|
||||||
|
if b.x > s.x then yaw = yaw +math.pi end
|
||||||
|
self.object:set_yaw(yaw)
|
||||||
|
self:set_velocity(self.follow_velocity)
|
||||||
|
self:set_animation("walk")
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:interact_with_door(action, target)
|
||||||
|
local p = self.object:get_pos()
|
||||||
|
--local t = minetest.get_timeofday()
|
||||||
|
--local dd = minetest.find_nodes_in_area(vector.offset(p,-1,-1,-1),vector.offset(p,1,1,1),{"group:door"})
|
||||||
|
--for _,d in pairs(dd) do
|
||||||
|
if target then
|
||||||
|
mcl_log("Door target is: ".. minetest.pos_to_string(target))
|
||||||
|
|
||||||
|
local n = minetest.get_node(target)
|
||||||
|
if n.name:find("_b_") or n.name:find("_t_") then
|
||||||
|
mcl_log("Door")
|
||||||
|
local def = minetest.registered_nodes[n.name]
|
||||||
|
local closed = n.name:find("_b_1") or n.name:find("_t_1")
|
||||||
|
--if self.state == PATHFINDING then
|
||||||
|
if closed and action == "open" and def.on_rightclick then
|
||||||
|
mcl_log("Open door")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
end
|
||||||
|
if not closed and action == "close" and def.on_rightclick then
|
||||||
|
mcl_log("Close door")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
end
|
||||||
|
--else
|
||||||
|
else
|
||||||
|
mcl_log("Not door")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mcl_log("no target. cannot try and open or close door")
|
||||||
|
end
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:do_pathfind_action(action)
|
||||||
|
if action then
|
||||||
|
mcl_log("Action present")
|
||||||
|
local type = action["type"]
|
||||||
|
local action_val = action["action"]
|
||||||
|
local target = action["target"]
|
||||||
|
if target then
|
||||||
|
mcl_log("Target: ".. minetest.pos_to_string(target))
|
||||||
|
end
|
||||||
|
if type and type == "door" then
|
||||||
|
mcl_log("Type is door")
|
||||||
|
self:interact_with_door(action_val, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mob_class:teleport(target)
|
||||||
|
if self.do_teleport then
|
||||||
|
if self.do_teleport(self, target) == false then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue