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)
|
||||
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)
|
||||
local function within_limits(pos, radius)
|
||||
local wmin, wmax = -30912, 30928
|
||||
|
@ -210,670 +170,6 @@ local node_ok = function(pos, fallback)
|
|||
return minetest.registered_nodes[fallback]
|
||||
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)
|
||||
-- returns true if mob has died
|
||||
local do_states = function(self, dtime)
|
||||
|
@ -926,7 +222,7 @@ local do_states = function(self, dtime)
|
|||
if self.walk_chance ~= 0
|
||||
and self.facing_fence ~= true
|
||||
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.state = "walk"
|
||||
|
@ -965,8 +261,8 @@ local do_states = function(self, dtime)
|
|||
local is_in_danger = false
|
||||
if lp then
|
||||
-- If mob in or on dangerous block, look for land
|
||||
if (is_node_dangerous(self, 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
|
||||
if (self:is_node_dangerous(self.standing_in) or
|
||||
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
|
||||
|
||||
-- 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
|
||||
local cliff_or_danger = false
|
||||
if is_in_danger then
|
||||
cliff_or_danger = is_at_cliff_or_danger(self)
|
||||
cliff_or_danger = self:is_at_cliff_or_danger()
|
||||
end
|
||||
if self.facing_fence == true
|
||||
or cliff_or_danger
|
||||
|
@ -1052,7 +348,7 @@ local do_states = function(self, dtime)
|
|||
|
||||
-- stop after 5 seconds or when at cliff
|
||||
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:set_velocity(0)
|
||||
self.state = "stand"
|
||||
|
@ -1285,7 +581,7 @@ local do_states = function(self, dtime)
|
|||
self:smart_mobs(s, p, dist, dtime)
|
||||
end
|
||||
|
||||
if is_at_cliff_or_danger(self) then
|
||||
if self:is_at_cliff_or_danger() then
|
||||
|
||||
self:set_velocity( 0)
|
||||
self:set_animation( "stand")
|
||||
|
@ -1486,15 +782,6 @@ local function check_herd(self,dtime)
|
|||
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
|
||||
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
|
||||
if def.after_activate then
|
||||
|
||||
def.after_activate(self, staticdata, def, dtime)
|
||||
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
|
||||
local mob_step = function(self, 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})
|
||||
end
|
||||
|
||||
check_aggro(self,dtime)
|
||||
self:check_aggro(dtime)
|
||||
self:check_item_pickup()
|
||||
|
||||
self:check_particlespawners(dtime)
|
||||
|
@ -2120,7 +1393,7 @@ local mob_step = function(self, dtime)
|
|||
end
|
||||
|
||||
--Mob following code.
|
||||
follow_flop(self)
|
||||
self:follow_flop()
|
||||
|
||||
--set animation speed relitive to velocity
|
||||
local v = self.object:get_velocity()
|
||||
|
@ -2314,13 +1587,11 @@ local mob_step = function(self, dtime)
|
|||
end
|
||||
|
||||
-- node replace check (cow eats grass etc.)
|
||||
replace(self, pos)
|
||||
self:replace(pos)
|
||||
end
|
||||
|
||||
self:monster_attack()
|
||||
|
||||
self:npc_attack()
|
||||
|
||||
self:check_breeding()
|
||||
|
||||
if do_states(self, dtime) then
|
||||
|
@ -2331,13 +1602,13 @@ local mob_step = function(self, dtime)
|
|||
return false
|
||||
end
|
||||
|
||||
do_jump(self)
|
||||
self:do_jump()
|
||||
|
||||
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
|
||||
self:set_velocity(0)
|
||||
self.state = "stand"
|
||||
|
@ -2389,7 +1660,7 @@ local mob_step = function(self, dtime)
|
|||
return
|
||||
end
|
||||
|
||||
if is_at_cliff_or_danger(self) then
|
||||
if self:is_at_cliff_or_danger() then
|
||||
self:set_velocity(0)
|
||||
self.state = "stand"
|
||||
self:set_animation( "stand")
|
||||
|
@ -2400,9 +1671,8 @@ end
|
|||
|
||||
|
||||
-- default function when mobs are blown up with TNT
|
||||
local do_tnt = function(obj, damage)
|
||||
|
||||
obj.object:punch(obj.object, 1.0, {
|
||||
local function do_tnt(damage)
|
||||
self.object:punch(self.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = damage},
|
||||
}, nil)
|
||||
|
@ -2449,6 +1719,9 @@ local create_mob_on_rightclick = function(on_rightclick)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--#### REGISTER FUNCS
|
||||
-- register mob entity
|
||||
function mcl_mobs.register_mob(name, def)
|
||||
|
||||
|
@ -2480,7 +1753,6 @@ if collisionbox[5] < 0.79 then
|
|||
end
|
||||
|
||||
minetest.register_entity(name, setmetatable({
|
||||
|
||||
use_texture_alpha = def.use_texture_alpha,
|
||||
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
|
||||
|
@ -2604,7 +1876,6 @@ minetest.register_entity(name, setmetatable({
|
|||
shooter_avoid_enemy = def.shooter_avoid_enemy,
|
||||
strafes = def.strafes,
|
||||
avoid_distance = def.avoid_distance or 9,
|
||||
teleport = teleport,
|
||||
do_teleport = def.do_teleport,
|
||||
spawn_class = def.spawn_class,
|
||||
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_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
|
||||
function mob_class:do_attack(player)
|
||||
|
||||
|
@ -307,7 +318,7 @@ function mob_class:monster_attack()
|
|||
if not damage_enabled
|
||||
or self.passive ~= false
|
||||
or self.state == "attack"
|
||||
or day_docile(self) then
|
||||
or self:day_docile() then
|
||||
return
|
||||
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!!
|
||||
self.object:remove()
|
||||
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())
|
||||
|
||||
--api and helpers
|
||||
-- effects: sounds and particles mostly
|
||||
dofile(path .. "/effects.lua")
|
||||
-- physics: involuntary mob movement - particularly falling and death
|
||||
dofile(path .. "/physics.lua")
|
||||
-- movement: general voluntary mob movement, walking avoiding cliffs etc.
|
||||
dofile(path .. "/movement.lua")
|
||||
-- items: item management for mobs
|
||||
dofile(path .. "/items.lua")
|
||||
-- pathfinding: pathfinding to target positions
|
||||
dofile(path .. "/pathfinding.lua")
|
||||
-- combat: attack logic
|
||||
dofile(path .. "/combat.lua")
|
||||
-- the enity functions themselves
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
|
||||
--utility functions
|
||||
dofile(path .. "/breeding.lua")
|
||||
dofile(path .. "/spawning.lua")
|
||||
|
|
|
@ -1,5 +1,58 @@
|
|||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
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)
|
||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||
|
@ -69,3 +122,655 @@ function mob_class:line_of_sight(pos1, pos2, stepsize)
|
|||
|
||||
return false
|
||||
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