Compare commits
44 Commits
658ac47d71
...
4c3b08c68d
Author | SHA1 | Date |
---|---|---|
kno10 | 4c3b08c68d | |
kno10 | 98600c87de | |
kno10 | a5cfaed843 | |
kno10 | 8c38745f6c | |
kno10 | 19c1863100 | |
kno10 | 250fc73e92 | |
kno10 | 9ea8945ff4 | |
kno10 | 488f61ba1b | |
kno10 | 16e17ba1ed | |
kno10 | 8ae66b5fad | |
kno10 | 8842e4224c | |
kno10 | a94b3a8d4a | |
kno10 | 8fce90111b | |
kno10 | 645f884e7f | |
kno10 | 46f7c29451 | |
kno10 | ddd180f5be | |
kno10 | b473d55644 | |
kno10 | 265dce301b | |
kno10 | 3538efbf52 | |
kno10 | ab673cdf8b | |
kno10 | 455353de27 | |
kno10 | b99e1a4b08 | |
kno10 | 0730cce236 | |
kno10 | 98f899fd67 | |
kno10 | 4b9e8aabb2 | |
kno10 | 936e47e5f9 | |
kno10 | 46b5416359 | |
kno10 | 72f825ade3 | |
kno10 | ec7e7c14bc | |
kno10 | f8a6da10cb | |
kno10 | 2e5b0bb3c7 | |
Mikita Wiśniewski | 41b188caea | |
kno10 | ae7995d195 | |
kno10 | e293cbe631 | |
the-real-herowl | fd6cac5f0c | |
teknomunk | e864cc19ed | |
teknomunk | 66c3c014a1 | |
teknomunk | 7807093b50 | |
teknomunk | f6c3f4bd16 | |
teknomunk | 96a03b1923 | |
teknomunk | 2145470f63 | |
teknomunk | 2ca0ccd8fe | |
teknomunk | 614518c6cd | |
kno10 | 253a06fa08 |
|
@ -73,7 +73,7 @@ functions needed for the mob to work properly which contains the following:
|
||||||
in lava (default: 8).
|
in lava (default: 8).
|
||||||
'fire_damage' holds the damage per second inflicted to mobs when standing
|
'fire_damage' holds the damage per second inflicted to mobs when standing
|
||||||
in fire (default: 1).
|
in fire (default: 1).
|
||||||
'light_damage' holds the damage per second inflicted to mobs when it's too
|
'light_damage'holds the damage per second inflicted to mobs when it's too
|
||||||
bright (above 13 light).
|
bright (above 13 light).
|
||||||
'suffocation' when true causes mobs to suffocate inside solid blocks (2 damage per second).
|
'suffocation' when true causes mobs to suffocate inside solid blocks (2 damage per second).
|
||||||
'floats' when set to 1 mob will float in water, 0 has them sink.
|
'floats' when set to 1 mob will float in water, 0 has them sink.
|
||||||
|
|
|
@ -93,7 +93,7 @@ end
|
||||||
|
|
||||||
-- Spawn a child
|
-- Spawn a child
|
||||||
function mcl_mobs.spawn_child(pos, mob_type)
|
function mcl_mobs.spawn_child(pos, mob_type)
|
||||||
local child = mcl_mobs.spawn(pos, mob_type)
|
local child = minetest.add_entity(pos, mob_type)
|
||||||
if not child then return end
|
if not child then return end
|
||||||
|
|
||||||
local ent = child:get_luaentity()
|
local ent = child:get_luaentity()
|
||||||
|
|
|
@ -230,7 +230,7 @@ function mob_class:set_animation(anim, fixed_frame)
|
||||||
y = a_end},
|
y = a_end},
|
||||||
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
|
self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
|
||||||
0, self.animation[anim .. "_loop"] ~= false)
|
0, self.animation[anim .. "_loop"] ~= false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function who_are_you_looking_at (self, dtime)
|
local function who_are_you_looking_at (self, dtime)
|
||||||
|
|
|
@ -37,22 +37,25 @@ function mob_class:stand()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Turn towards a (nearby) target, primarily for path following
|
-- Turn towards a (nearby) target, primarily for path following
|
||||||
function mob_class:go_to_pos(b)
|
function mob_class:go_to_pos(b, speed)
|
||||||
|
if not self then return end
|
||||||
if not b then return end
|
if not b then return end
|
||||||
local s = self.object:get_pos()
|
local s = self.object:get_pos()
|
||||||
if vector_distance(b,s) < .1 then return true end
|
if vector_distance(b,s) < .4 then return true end
|
||||||
if b.y > s.y then self:do_jump() end
|
if b.y > s.y + 0.2 then self:do_jump() end
|
||||||
self:turn_in_direction(b.x - s.x, b.z - s.z, 5)
|
self:turn_in_direction(b.x - s.x, b.z - s.z, 2)
|
||||||
self:set_velocity(self.walk_velocity)
|
speed = speed or self.walk_velocity
|
||||||
self:set_animation("walk")
|
self:set_velocity(speed)
|
||||||
|
self:set_animation(speed <= self.walk_velocity and "walk" or "run")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Returns true is node can deal damage to self, except water damage
|
-- Returns true is node can deal damage to self, except water damage
|
||||||
function mob_class:is_node_dangerous(nodename)
|
function mob_class:is_node_dangerous(nodename)
|
||||||
local ndef = minetest.registered_nodes[nodename]
|
local ndef = minetest.registered_nodes[nodename]
|
||||||
return ndef
|
return ndef
|
||||||
and ((self.lava_damage > 0 and ndef.groups.lava)
|
and ((self.lava_damage > 0 and (ndef.groups.lava or 0) > 0)
|
||||||
or (self.fire_damage > 0 and ndef.groups.fire)
|
or (self.fire_damage > 0 and (ndef.groups.fire or 0) > 0)
|
||||||
or ((ndef.damage_per_second or 0) > 0))
|
or ((ndef.damage_per_second or 0) > 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -65,12 +68,17 @@ function mob_class:is_node_waterhazard(nodename)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:target_visible(origin)
|
function mob_class:target_visible(origin)
|
||||||
if not origin then return false end
|
if not origin then return end
|
||||||
if not self.attack then return false end
|
if not self.attack then return end
|
||||||
local target_pos = self.attack:get_pos()
|
local target_pos = self.attack:get_pos()
|
||||||
if not target_pos then return false end
|
if not target_pos then return end
|
||||||
|
|
||||||
local origin_eye_pos = vector_offset(origin, 0, self.head_eye_height, 0)
|
local origin_eye_pos = vector_offset(origin, 0, self.head_eye_height, 0)
|
||||||
|
|
||||||
|
--minetest.log("origin: " .. dump(origin))
|
||||||
|
--minetest.log("origin_eye_pos: " .. dump(origin_eye_pos))
|
||||||
|
|
||||||
|
local targ_head_height, targ_feet_height
|
||||||
local cbox = self.collisionbox
|
local cbox = self.collisionbox
|
||||||
-- TODO also worth testing midway between feet and head?
|
-- TODO also worth testing midway between feet and head?
|
||||||
-- to top of entity
|
-- to top of entity
|
||||||
|
@ -81,11 +89,23 @@ function mob_class:target_visible(origin)
|
||||||
else
|
else
|
||||||
if line_of_sight(origin_eye_pos, vector_offset(target_pos, 0, cbox[2], 0), self.see_through_opaque or mobs_see_through_opaque, true) then return true end
|
if line_of_sight(origin_eye_pos, vector_offset(target_pos, 0, cbox[2], 0), self.see_through_opaque or mobs_see_through_opaque, true) then return true end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--minetest.log("start targ_head_height: " .. dump(targ_head_height))
|
||||||
|
if raycast_line_of_sight (origin_eye_pos, targ_head_height) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--minetest.log("Start targ_feet_height: " .. dump(targ_feet_height))
|
||||||
|
if raycast_line_of_sight (origin_eye_pos, targ_feet_height) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO mid way between feet and head
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check line of sight
|
-- check line of sight
|
||||||
-- @param stepsize is ignored now
|
|
||||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||||
return line_of_sight(pos1, pos2, self.see_through_opaque or mobs_see_through_opaque, true)
|
return line_of_sight(pos1, pos2, self.see_through_opaque or mobs_see_through_opaque, true)
|
||||||
end
|
end
|
||||||
|
@ -117,9 +137,10 @@ function mob_class:can_jump_cliff()
|
||||||
--minetest.log("Jumping cliff: " .. self.name .. " nodes " .. node_low.name .. " - " .. node_far.name .. " - " .. node_far2.name)
|
--minetest.log("Jumping cliff: " .. self.name .. " nodes " .. node_low.name .. " - " .. node_far.name .. " - " .. node_far2.name)
|
||||||
minetest.after(.1, function() if self and self.object then self._jumping_cliff = false end end)
|
minetest.after(.1, function() if self and self.object then self._jumping_cliff = false end end)
|
||||||
return true
|
return true
|
||||||
|
else
|
||||||
|
self._jumping_cliff = false
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
self._jumping_cliff = false
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- is mob facing a cliff or danger
|
-- is mob facing a cliff or danger
|
||||||
|
@ -131,7 +152,8 @@ function mob_class:is_at_cliff_or_danger()
|
||||||
if self.fly then return false end -- also avoids checking fish
|
if self.fly then return false end -- also avoids checking fish
|
||||||
local pos, yaw = self.object:get_pos(), self.object:get_yaw()
|
local pos, yaw = self.object:get_pos(), self.object:get_yaw()
|
||||||
local cbox = self.collisionbox
|
local cbox = self.collisionbox
|
||||||
local dir_x, dir_z = -sin(yaw) * (cbox[4] + 0.5), cos(yaw) * (cbox[4] + 0.5)
|
local dir_x = -sin(yaw) * (cbox[4] + 0.5)
|
||||||
|
local dir_z = cos(yaw) * (cbox[4] + 0.5)
|
||||||
|
|
||||||
local ypos = pos.y + cbox[2] + 0.1 -- just above floor
|
local ypos = pos.y + cbox[2] + 0.1 -- just above floor
|
||||||
|
|
||||||
|
@ -139,7 +161,9 @@ function mob_class:is_at_cliff_or_danger()
|
||||||
vector_new(pos.x + dir_x, ypos, pos.z + dir_z),
|
vector_new(pos.x + dir_x, ypos, pos.z + dir_z),
|
||||||
vector_new(pos.x + dir_x, floor(ypos - self.fear_height), pos.z + dir_z))
|
vector_new(pos.x + dir_x, floor(ypos - self.fear_height), pos.z + dir_z))
|
||||||
|
|
||||||
if free_fall then return "free fall" end
|
if free_fall then
|
||||||
|
return "free fall"
|
||||||
|
end
|
||||||
local height = ypos + 0.4 - blocker.y
|
local height = ypos + 0.4 - blocker.y
|
||||||
local chance = self.jump_height / (height * height)
|
local chance = self.jump_height / (height * height)
|
||||||
if height >= self.fear_height and random() < chance then
|
if height >= self.fear_height and random() < chance then
|
||||||
|
@ -153,15 +177,22 @@ function mob_class:is_at_cliff_or_danger()
|
||||||
if self:is_node_dangerous(self.standing_in.name) or self:is_node_waterhazard(self.standing_in.name) then
|
if self:is_node_dangerous(self.standing_in.name) or self:is_node_waterhazard(self.standing_in.name) then
|
||||||
return false -- allow to get out of the immediate danger
|
return false -- allow to get out of the immediate danger
|
||||||
end
|
end
|
||||||
if self:is_node_dangerous(bnode.name) or self:is_node_waterhazard(bnode.name) then return bnode.name end
|
if self:is_node_dangerous(bnode.name) or self:is_node_waterhazard(bnode.name) then
|
||||||
|
return bnode.name
|
||||||
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water
|
-- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water
|
||||||
function mob_class:is_at_water_danger()
|
function mob_class:is_at_water_danger()
|
||||||
if self.water_damage == 0 and self.breath_max == -1 then return false end
|
if self.water_damage == 0 and self.breath_max == -1 then
|
||||||
if self.fly then return false end -- also avoids checking fish
|
--minetest.log("Do not need a water check for: " .. self.name)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if self.fly then -- also avoids checking fish
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local in_water_danger = self:is_node_waterhazard(self.standing_in.name) or self:is_node_waterhazard(self.standing_on.name)
|
local in_water_danger = self:is_node_waterhazard(self.standing_in.name) or self:is_node_waterhazard(self.standing_on.name)
|
||||||
if in_water_danger then return false end -- If you're in trouble, do not stop
|
if in_water_danger then return false end -- If you're in trouble, do not stop
|
||||||
|
@ -170,7 +201,8 @@ function mob_class:is_at_water_danger()
|
||||||
|
|
||||||
local pos, yaw = self.object:get_pos(), self.object:get_yaw()
|
local pos, yaw = self.object:get_pos(), self.object:get_yaw()
|
||||||
local cbox = self.collisionbox
|
local cbox = self.collisionbox
|
||||||
local dir_x, dir_z = -sin(yaw) * (cbox[4] + 0.5), cos(yaw) * (cbox[4] + 0.5)
|
local dir_x = -sin(yaw) * (cbox[4] + 0.5)
|
||||||
|
local dir_z = cos(yaw) * (cbox[4] + 0.5)
|
||||||
|
|
||||||
local ypos = pos.y + cbox[2] + 0.1 -- just above floor
|
local ypos = pos.y + cbox[2] + 0.1 -- just above floor
|
||||||
|
|
||||||
|
@ -180,7 +212,10 @@ function mob_class:is_at_water_danger()
|
||||||
|
|
||||||
if not los then
|
if not los then
|
||||||
local bnode = minetest.get_node(blocker)
|
local bnode = minetest.get_node(blocker)
|
||||||
if self:is_node_waterhazard(bnode.name) then return bnode.name end
|
local waterdanger = self:is_node_waterhazard(bnode.name)
|
||||||
|
if waterdanger then
|
||||||
|
return bnode.name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -245,7 +280,16 @@ function mob_class:do_jump()
|
||||||
|
|
||||||
-- what is in front of mob?
|
-- what is in front of mob?
|
||||||
local nod = minetest.get_node(vector_offset(pos, dir_x, 0.5, dir_z)).name
|
local nod = minetest.get_node(vector_offset(pos, dir_x, 0.5, dir_z)).name
|
||||||
if nod == NODE_SNOW then return false end -- no need to jump
|
local ndef = minetest.registered_nodes[nod.name]
|
||||||
|
-- thin blocks that do not need to be jumped
|
||||||
|
if nod.name == NODE_SNOW or (ndef and ndef.groups.carpet or 0) > 0 then return false end
|
||||||
|
-- nothing to jump on?
|
||||||
|
if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then return false end
|
||||||
|
-- facing a fence? jumping will not help (FIXME: consider jump height)
|
||||||
|
if (ndef.groups.fence or 0) ~= 0 or (ndef.groups.fence_gate or 0) ~= 0 or (ndef.groups.wall or 0) ~= 0 then
|
||||||
|
self.facing_fence = true
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- this is used to detect if there's a block on top of the block in front of the mob.
|
-- 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.
|
-- If there is, there is no point in jumping as we won't manage.
|
||||||
|
@ -254,16 +298,9 @@ function mob_class:do_jump()
|
||||||
|
|
||||||
-- we don't attempt to jump if there's a stack of blocks blocking, unless attacking
|
-- we don't attempt to jump if there's a stack of blocks blocking, unless attacking
|
||||||
local ntdef = minetest.registered_nodes[node_top]
|
local ntdef = minetest.registered_nodes[node_top]
|
||||||
|
-- TODO: snow, carpet?
|
||||||
if ntdef and ntdef.walkable == true --[[and not (self.attack and self.state == "attack")]] then return false end
|
if ntdef and ntdef.walkable == true --[[and not (self.attack and self.state == "attack")]] then return false end
|
||||||
|
|
||||||
local ndef = minetest.registered_nodes[nod]
|
|
||||||
if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then return false end
|
|
||||||
|
|
||||||
if ndef.groups.fence or ndef.groups.fence_gate or ndef.groups.wall then
|
|
||||||
self.facing_fence = true
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
v.y = math.min(v.y, 0) + math.sqrt(self.jump_height * 20 + (in_water or self._can_jump_cliff and 10 or 0))
|
v.y = math.min(v.y, 0) + math.sqrt(self.jump_height * 20 + (in_water or self._can_jump_cliff and 10 or 0))
|
||||||
v.y = math.min(-self.fall_speed, math.max(v.y, self.fall_speed))
|
v.y = math.min(-self.fall_speed, math.max(v.y, self.fall_speed))
|
||||||
self.object:set_velocity(v)
|
self.object:set_velocity(v)
|
||||||
|
@ -277,7 +314,7 @@ function mob_class:do_jump()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if we jumped against a block/wall 4 times then turn
|
-- if we jumped against a block/wall 4 times then turn
|
||||||
if v.x * v.x + v.z * v.z < 0.1 then
|
if (v.x * v.x + v.z * v.z) < 0.1 then
|
||||||
self._jump_count = (self._jump_count or 0) + 1
|
self._jump_count = (self._jump_count or 0) + 1
|
||||||
if self._jump_count == 4 then
|
if self._jump_count == 4 then
|
||||||
self:turn_by(TWOPI * (random() - 0.5), 8)
|
self:turn_by(TWOPI * (random() - 0.5), 8)
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
local mob_class = mcl_mobs.mob_class
|
local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
local PATHFINDING_FAIL_THRESHOLD = 100 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door
|
local PATHFINDING_FAIL_THRESHOLD = 200 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door
|
||||||
local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again
|
local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again
|
||||||
local PATHING_START_DELAY = 4 -- When doing non-prioritised pathing, how long to wait until last mob pathed
|
local PATHING_START_DELAY = 4 -- When doing non-prioritised pathing, how long to wait until last mob pathed
|
||||||
|
|
||||||
local PATHFINDING_SEARCH_DISTANCE = 50 -- How big the square is that pathfinding will look
|
local PATHFINDING_SEARCH_DISTANCE = 25 -- How big the square is that pathfinding will look
|
||||||
|
|
||||||
local PATHFINDING = "gowp"
|
local PATHFINDING = "gowp"
|
||||||
|
|
||||||
local one_down = vector.new(0,-1,0)
|
|
||||||
local one_up = vector.new(0,1,0)
|
|
||||||
|
|
||||||
local plane_adjacents = {
|
local plane_adjacents = {
|
||||||
vector.new(1,0,0),
|
vector.new(1,0,0),
|
||||||
vector.new(-1,0,0),
|
vector.new(-1,0,0),
|
||||||
|
@ -20,6 +17,7 @@ local plane_adjacents = {
|
||||||
}
|
}
|
||||||
|
|
||||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false)
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false)
|
||||||
|
local visualize = minetest.settings:get_bool("mcl_mobs_pathfinding_visualize",false)
|
||||||
|
|
||||||
local LOG_MODULE = "[Mobs Pathfinding]"
|
local LOG_MODULE = "[Mobs Pathfinding]"
|
||||||
local function mcl_log (message)
|
local function mcl_log (message)
|
||||||
|
@ -68,33 +66,22 @@ end
|
||||||
-- an action, such as to open or close a door where we know that pos requires that action
|
-- an action, such as to open or close a door where we know that pos requires that action
|
||||||
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
|
local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos)
|
||||||
local wp_out = {}
|
local wp_out = {}
|
||||||
|
|
||||||
-- TODO Just pass in door position and the index before is open, the index after is close
|
|
||||||
local current_door_index = -1
|
|
||||||
|
|
||||||
for i, cur_pos in pairs(wp_in) do
|
for i, cur_pos in pairs(wp_in) do
|
||||||
local action = nil
|
local action = nil
|
||||||
|
|
||||||
local cur_pos_to_add = vector.add(cur_pos, one_down)
|
if door_open_pos and vector.equals(cur_pos, door_open_pos) then
|
||||||
if door_open_pos and vector.equals (cur_pos, door_open_pos) then
|
|
||||||
mcl_log ("Door open match")
|
mcl_log ("Door open match")
|
||||||
action = {type = "door", action = "open", target = cur_door_pos}
|
action = {type = "door", action = "open", target = cur_door_pos}
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
|
elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then
|
||||||
mcl_log ("Door close match")
|
mcl_log ("Door close match")
|
||||||
action = {type = "door", action = "close", target = cur_door_pos}
|
action = {type = "door", action = "close", target = cur_door_pos}
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
|
elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then
|
||||||
mcl_log("Current door pos")
|
mcl_log("Current door pos")
|
||||||
action = {type = "door", action = "open", target = cur_door_pos}
|
action = {type = "door", action = "open", target = cur_door_pos}
|
||||||
cur_pos_to_add = vector.add(cur_pos, one_down)
|
|
||||||
else
|
|
||||||
cur_pos_to_add = cur_pos
|
|
||||||
--mcl_log ("Pos doesn't match")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
wp_out[i] = {}
|
wp_out[i] = {}
|
||||||
wp_out[i]["pos"] = cur_pos_to_add
|
wp_out[i]["pos"] = cur_pos
|
||||||
wp_out[i]["failed_attempts"] = 0
|
wp_out[i]["failed_attempts"] = 0
|
||||||
wp_out[i]["action"] = action
|
wp_out[i]["action"] = action
|
||||||
|
|
||||||
|
@ -124,77 +111,66 @@ end
|
||||||
|
|
||||||
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
|
-- This function is used to see if we can path. We could use to check a route, rather than making people move.
|
||||||
local function calculate_path_through_door (p, cur_door_pos, t)
|
local function calculate_path_through_door (p, cur_door_pos, t)
|
||||||
|
if not cur_door_pos then return end
|
||||||
if t then
|
if t then
|
||||||
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t))
|
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. " through " .. minetest.pos_to_string(cur_door_pos) .. ", to target: " .. minetest.pos_to_string(t))
|
||||||
else
|
else
|
||||||
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p))
|
mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. " through " .. minetest.pos_to_string(cur_door_pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
local enriched_path = nil
|
for _,v in pairs(plane_adjacents) do
|
||||||
local wp, prospective_wp
|
local pos_closest_to_door = vector.add(cur_door_pos,v)
|
||||||
|
local n = minetest.get_node(pos_closest_to_door)
|
||||||
|
if not n.walkable then
|
||||||
|
mcl_log("We have open space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
|
||||||
|
|
||||||
local pos_closest_to_door = nil
|
local prospective_wp = minetest.find_path(p, pos_closest_to_door, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||||
local other_side_of_door = nil
|
|
||||||
|
|
||||||
if cur_door_pos then
|
if prospective_wp then
|
||||||
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
|
local other_side_of_door = vector.add(cur_door_pos,-v)
|
||||||
|
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
||||||
|
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
||||||
|
|
||||||
for _,v in pairs(plane_adjacents) do
|
table.insert(prospective_wp, cur_door_pos)
|
||||||
pos_closest_to_door = vector.add(cur_door_pos,v)
|
|
||||||
other_side_of_door = vector.add(cur_door_pos,-v)
|
|
||||||
|
|
||||||
local n = minetest.get_node(pos_closest_to_door)
|
if t then
|
||||||
|
mcl_log("We have t, lets go from door to target")
|
||||||
|
local wp_otherside_door_to_target = minetest.find_path(other_side_of_door, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||||
|
|
||||||
if n.name == "air" then
|
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
||||||
mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
|
append_paths (prospective_wp, wp_otherside_door_to_target)
|
||||||
|
mcl_log("We have a path from outside door to target")
|
||||||
prospective_wp = minetest.find_path(p, pos_closest_to_door, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
return generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||||
|
|
||||||
if prospective_wp then
|
|
||||||
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
|
||||||
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
|
||||||
|
|
||||||
table.insert(prospective_wp, cur_door_pos)
|
|
||||||
|
|
||||||
if t then
|
|
||||||
mcl_log("We have t, lets go from door to target")
|
|
||||||
local wp_otherside_door_to_target = minetest.find_path(other_side_of_door, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
|
||||||
|
|
||||||
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
|
||||||
append_paths (prospective_wp, wp_otherside_door_to_target)
|
|
||||||
|
|
||||||
wp = prospective_wp
|
|
||||||
mcl_log("We have a path from outside door to target")
|
|
||||||
else
|
|
||||||
mcl_log("We cannot path from outside door to target")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
mcl_log("No t, just add other side of door")
|
mcl_log("We cannot path from outside door to target")
|
||||||
table.insert(prospective_wp, other_side_of_door)
|
|
||||||
wp = prospective_wp
|
|
||||||
end
|
|
||||||
|
|
||||||
if wp then
|
|
||||||
enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
mcl_log("Cannot path to this air block next to door.")
|
mcl_log("No t, just add other side of door")
|
||||||
|
table.insert(prospective_wp, other_side_of_door)
|
||||||
|
return generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
mcl_log("Cannot path to this air block next to door.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
|
||||||
mcl_log("No door found")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if wp and not enriched_path then
|
|
||||||
mcl_log("Wp but not enriched")
|
|
||||||
enriched_path = generate_enriched_path(wp)
|
|
||||||
end
|
|
||||||
return enriched_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- we treat ignore as solid, as we cannot path there
|
||||||
|
local function is_solid(pos)
|
||||||
|
local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||||
|
return (not ndef) or ndef.walkable
|
||||||
|
end
|
||||||
|
|
||||||
|
local function find_open_node(pos, radius)
|
||||||
|
local r = vector.round(pos)
|
||||||
|
if not is_solid(r) then return r end
|
||||||
|
local above = vector.offset(r, 0, 1, 0)
|
||||||
|
if not is_solid(above) then return above, true end -- additional return: drop last
|
||||||
|
local n = minetest.find_node_near(pos, radius or 1, {"air"})
|
||||||
|
if n then return n end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function mob_class:gopath(target, callback_arrived, prioritised)
|
function mob_class:gopath(target, callback_arrived, prioritised)
|
||||||
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
||||||
|
@ -204,8 +180,19 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
||||||
|
|
||||||
self.order = nil
|
self.order = nil
|
||||||
|
|
||||||
local p = self.object:get_pos()
|
-- maybe feet are buried in solid?
|
||||||
local t = vector.offset(target,0,1,0)
|
local start = self.object:get_pos()
|
||||||
|
local p = find_open_node(start, 1)
|
||||||
|
if not p then -- buried?
|
||||||
|
minetest.log("action", "Cannot path from "..minetest.pos_to_string(start).." because it is solid. Nodetype: "..minetest.get_node(start).name)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- target might be a job-site that is solid
|
||||||
|
local t, drop_last_wp = find_open_node(target, 1)
|
||||||
|
if not t then
|
||||||
|
minetest.log("action", "Cannot path to "..minetest.pos_to_string(target).." because it is solid. Nodetype: "..minetest.get_node(target).name)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
--Check direct route
|
--Check direct route
|
||||||
local wp = minetest.find_path(p, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
local wp = minetest.find_path(p, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||||
|
@ -213,11 +200,15 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
||||||
if not wp then
|
if not wp then
|
||||||
mcl_log("### No direct path. Path through door closest to target.")
|
mcl_log("### No direct path. Path through door closest to target.")
|
||||||
local door_near_target = minetest.find_node_near(target, 16, {"group:door"})
|
local door_near_target = minetest.find_node_near(target, 16, {"group:door"})
|
||||||
|
local below = door_near_target and vector.offset(door_near_target, 0, -1, 0)
|
||||||
|
if below and minetest.get_item_group(minetest.get_node(below), "door") > 0 then door_near_target = below end
|
||||||
wp = calculate_path_through_door(p, door_near_target, t)
|
wp = calculate_path_through_door(p, door_near_target, t)
|
||||||
|
|
||||||
if not wp then
|
if not wp then
|
||||||
mcl_log("### No path though door closest to target. Try door closest to origin.")
|
mcl_log("### No path though door closest to target. Try door closest to origin.")
|
||||||
local door_closest = minetest.find_node_near(p, 16, {"group:door"})
|
local door_closest = minetest.find_node_near(p, 16, {"group:door"})
|
||||||
|
local below = door_closest and vector.offset(door_closest, 0, -1, 0)
|
||||||
|
if below and minetest.get_item_group(minetest.get_node(below), "door") > 0 then door_closest = below end
|
||||||
wp = calculate_path_through_door(p, door_closest, t)
|
wp = calculate_path_through_door(p, door_closest, t)
|
||||||
|
|
||||||
-- Path through 2 doors
|
-- Path through 2 doors
|
||||||
|
@ -231,7 +222,7 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
||||||
|
|
||||||
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
|
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
|
||||||
if pos_after_door_entry then
|
if pos_after_door_entry then
|
||||||
local pos_after_door = vector.add(pos_after_door_entry["pos"], one_up)
|
local pos_after_door = pos_after_door_entry["pos"]
|
||||||
mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door))
|
mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door))
|
||||||
local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t)
|
local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t)
|
||||||
if path_after_door and #path_after_door > 1 then
|
if path_after_door and #path_after_door > 1 then
|
||||||
|
@ -263,20 +254,92 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
||||||
-- If cannot path, don't immediately try again
|
-- If cannot path, don't immediately try again
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- todo: we would also need to avoid overhangs, but minetest.find_path cannot help us there
|
||||||
|
-- we really need a better pathfinder overall.
|
||||||
|
|
||||||
|
-- try to find a way around fences and walls. This is very barebones, but at least it should
|
||||||
|
-- help path around very simple fences *IF* there is a detour that does not require jumping or gates.
|
||||||
if wp and #wp > 0 then
|
if wp and #wp > 0 then
|
||||||
|
local i = 1
|
||||||
|
while i < #wp do
|
||||||
|
-- fence or wall underneath?
|
||||||
|
local bdef = minetest.registered_nodes[minetest.get_node(vector.offset(wp[i].pos, 0, -1, 0)).name]
|
||||||
|
if not bdef then minetest.log("warning", "There must not be unknown nodes on path") end
|
||||||
|
-- carpets are fine
|
||||||
|
if bdef and (bdef.groups.carpet or 0) > 0 then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
-- target bottom of door
|
||||||
|
elseif bdef and (bdef.groups.door or 0) > 0 then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
-- not walkable?
|
||||||
|
elseif bdef and not bdef.walkable then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
i = i - 1
|
||||||
|
-- plan opening fence gates
|
||||||
|
elseif bdef and (bdef.groups.fence_gate or 0) > 0 then
|
||||||
|
wp[i].pos = vector.offset(wp[i].pos, 0, -1, 0)
|
||||||
|
wp[math.max(1,i-1)].action = {type = "door", action = "open", target = wp[i].pos}
|
||||||
|
if i+1 < #wp then
|
||||||
|
wp[i+1].action = {type = "door", action = "close", target = wp[i].pos}
|
||||||
|
end
|
||||||
|
-- do not jump on fences and walls, but try to walk around
|
||||||
|
elseif bdef and i > 1 and ((bdef.groups.fence or 0) > 0 or (bdef.groups.wall or 0) > 0) and wp[i].pos.y > wp[i-1].pos.y then
|
||||||
|
-- find end of wall(s)
|
||||||
|
local j = i + 1
|
||||||
|
while j <= #wp do
|
||||||
|
local below = vector.offset(wp[j].pos, 0, -1, 0)
|
||||||
|
local bdef = minetest.registered_nodes[minetest.get_node(below).name]
|
||||||
|
if not bdef or ((bdef.groups.fence or 0) == 0 and (bdef.groups.wall or 0) == 0) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
minetest.log("warning", bdef.name .. " at "..tostring(i).." end at "..(j <= #wp and tostring(j) or "nil"))
|
||||||
|
if j <= #wp and wp[i-1].pos.y == wp[j].pos.y then
|
||||||
|
local swp = minetest.find_path(wp[i-1].pos, wp[j].pos, PATHFINDING_SEARCH_DISTANCE, 0, 0)
|
||||||
|
-- TODO: if we do not find a path here, consider pathing through a fence gate!
|
||||||
|
if swp and #swp > 0 then
|
||||||
|
for k = j-1,i,-1 do table.remove(wp, k) end
|
||||||
|
for k = 2, #swp-1 do table.insert(wp, i-2+k, {pos = swp[k], failed_attempts = 0}) end
|
||||||
|
minetest.log("warning", "Monkey patch pathfinding around "..bdef.name.." successful.")
|
||||||
|
i = i + #swp - 4
|
||||||
|
else
|
||||||
|
minetest.log("warning", "Monkey patch pathfinding around "..bdef.name.." failed.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if wp and drop_last_wp and vector.equals(wp[#wp], t) then table.remove(wp, #wp) end
|
||||||
|
if wp and #wp > 0 then
|
||||||
|
if visualize then
|
||||||
|
for i = 1,#wp do
|
||||||
|
core.add_particle({pos = wp[i].pos, expirationtime=3+i/3, size=3+2/i, velocity=vector.new(0,-0.02,0),
|
||||||
|
texture="mcl_copper_anti_oxidation_particle.png"}) -- white stars
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--output_table(wp)
|
--output_table(wp)
|
||||||
self._target = t
|
self._target = t
|
||||||
self.callback_arrived = callback_arrived
|
self.callback_arrived = callback_arrived
|
||||||
self.current_target = table.remove(wp,1)
|
self.current_target = table.remove(wp,1)
|
||||||
self.waypoints = wp
|
while self.current_target and self.current_target.pos and vector.distance(p, self.current_target.pos) < 0.5 do
|
||||||
self.state = PATHFINDING
|
--mcl_log("Skipping close initial waypoint")
|
||||||
return true
|
self.current_target = table.remove(wp,1)
|
||||||
else
|
end
|
||||||
self.state = "walk"
|
if self.current_target and self.current_target.pos then
|
||||||
self.waypoints = nil
|
self:turn_in_direction(self.current_target.pos.x - p.x, self.current_target.pos.z - p.z, 2)
|
||||||
self.current_target = nil
|
self.waypoints = wp
|
||||||
-- minetest.log("no path found")
|
self.state = PATHFINDING
|
||||||
|
return true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
self:turn_in_direction(target.x - p.x, target.z - p.z, 4)
|
||||||
|
self.state = "walk"
|
||||||
|
self.waypoints = nil
|
||||||
|
self.current_target = nil
|
||||||
|
--minetest.log("no path found")
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:interact_with_door(action, target)
|
function mob_class:interact_with_door(action, target)
|
||||||
|
@ -295,11 +358,21 @@ function mob_class:interact_with_door(action, target)
|
||||||
if closed and action == "open" and def.on_rightclick then
|
if closed and action == "open" and def.on_rightclick then
|
||||||
mcl_log("Open door")
|
mcl_log("Open door")
|
||||||
def.on_rightclick(target,n,self)
|
def.on_rightclick(target,n,self)
|
||||||
end
|
elseif not closed and action == "close" and def.on_rightclick then
|
||||||
if not closed and action == "close" and def.on_rightclick then
|
|
||||||
mcl_log("Close door")
|
mcl_log("Close door")
|
||||||
def.on_rightclick(target,n,self)
|
def.on_rightclick(target,n,self)
|
||||||
end
|
end
|
||||||
|
elseif n.name:find("_gate") then
|
||||||
|
local def = minetest.registered_nodes[n.name]
|
||||||
|
local meta = minetest.get_meta(target)
|
||||||
|
local closed = meta:get_int("state") == 0
|
||||||
|
if closed and action == "open" and def.on_rightclick then
|
||||||
|
mcl_log("Open gate")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
elseif not closed and action == "close" and def.on_rightclick then
|
||||||
|
mcl_log("Close gate")
|
||||||
|
def.on_rightclick(target,n,self)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
mcl_log("Not door")
|
mcl_log("Not door")
|
||||||
end
|
end
|
||||||
|
@ -355,53 +428,56 @@ function mob_class:check_gowp(dtime)
|
||||||
|
|
||||||
-- More pathing to be done
|
-- More pathing to be done
|
||||||
local distance_to_current_target = 50
|
local distance_to_current_target = 50
|
||||||
if self.current_target and self.current_target["pos"] then
|
if self.current_target and self.current_target.pos then
|
||||||
local dx, dy, dz = self.current_target["pos"].x-p.x, self.current_target["pos"].y-p.y, self.current_target["pos"].z-p.z
|
local dx, dy, dz = self.current_target.pos.x-p.x, self.current_target.pos.y-p.y, self.current_target.pos.z-p.z
|
||||||
distance_to_current_target = (dx*dx+dy*dy*0.25+dz*dz)^0.5 -- reduced weight on y
|
distance_to_current_target = (dx*dx+dy*dy*0.5+dz*dz)^0.5 -- reduced weight on y
|
||||||
--distance_to_current_target = vector.distance(p,self.current_target["pos"])
|
--distance_to_current_target = vector.distance(p,self.current_target.pos)
|
||||||
end
|
end
|
||||||
-- also check next target, maybe we were too fast
|
-- also check next target, maybe we were too fast
|
||||||
local next_target = #self.waypoints > 1 and self.waypoints[1]
|
local next_target = #self.waypoints > 1 and self.waypoints[1]
|
||||||
if not self.current_target["action"] and next_target and next_target["pos"] and distance_to_current_target < 1.5 then
|
if not self.current_target["action"] and next_target and next_target.pos and distance_to_current_target < 1.5 then
|
||||||
local dx, dy, dz = next_target["pos"].x-p.x, next_target["pos"].y-p.y, next_target["pos"].z-p.z
|
local dx, dy, dz = next_target.pos.x-p.x, next_target.pos.y-p.y, next_target.pos.z-p.z
|
||||||
local distance_to_next_target = (dx*dx+dy*dy*0.25+dz*dz)^0.5 -- reduced weight on y
|
local distance_to_next_target = (dx*dx+dy*dy*0.5+dz*dz)^0.5 -- reduced weight on y
|
||||||
if distance_to_next_target < distance_to_current_target then
|
if distance_to_next_target < distance_to_current_target then
|
||||||
mcl_log("Skipped one waypoint.")
|
mcl_log("Skipped one waypoint.")
|
||||||
self.current_target = table.remove(self.waypoints, 1) -- pop waypoint already
|
self.current_target = table.remove(self.waypoints, 1) -- pop waypoint already
|
||||||
distance_to_current_target = distance_to_next_target
|
distance_to_current_target = distance_to_next_target
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
-- debugging tool
|
||||||
|
if visualize and self.current_target and self.current_target.pos then
|
||||||
|
core.add_particle({pos = self.current_target.pos, expirationtime=.1, size=3, velocity=vector.new(0,-0.2,0), texture="mcl_particles_flame.png"})
|
||||||
|
end
|
||||||
|
|
||||||
-- 0.6 is working but too sensitive. sends villager back too frequently. 0.7 is quite good, but not with heights
|
-- 0.6 is working but too sensitive. sends villager back too frequently. 0.7 is quite good, but not with heights
|
||||||
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
|
-- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning
|
||||||
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
|
-- 0.9 and 1.0 is also good. Stick with unless door open or closing issues
|
||||||
local threshold = self.current_target["action"] and 0.8 or 1.2
|
local threshold = self.current_target["action"] and 0.7 or 0.9
|
||||||
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < threshold ) then
|
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target.pos or distance_to_current_target < threshold ) then
|
||||||
-- We have waypoints, and are at current_target or have no current target. We need a new current_target.
|
-- We have waypoints, and are at current_target or have no current target. We need a new current_target.
|
||||||
self:do_pathfind_action (self.current_target["action"])
|
self:do_pathfind_action (self.current_target["action"])
|
||||||
|
|
||||||
local failed_attempts = self.current_target["failed_attempts"]
|
local failed_attempts = self.current_target["failed_attempts"]
|
||||||
mcl_log("There after " .. failed_attempts .. " failed attempts. current target:".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: " .. distance_to_current_target)
|
mcl_log("There after " .. failed_attempts .. " failed attempts. current target:".. minetest.pos_to_string(self.current_target.pos) .. ". Distance: " .. distance_to_current_target)
|
||||||
|
|
||||||
|
local hurry = (self.order == "sleep" or #self.waypoints > 15) and self.run_velocity or self.walk_velocity
|
||||||
self.current_target = table.remove(self.waypoints, 1)
|
self.current_target = table.remove(self.waypoints, 1)
|
||||||
-- use smoothing
|
-- use smoothing -- TODO: check for blockers before cutting corners?
|
||||||
--[[if #self.waypoints > 0 and not self.current_target["action"] then
|
if #self.waypoints > 0 and not self.current_target["action"] then
|
||||||
local curwp, nextwp = self.current_target["pos"], self.waypoints[1]["pos"]
|
local curwp, nextwp = self.current_target.pos, self.waypoints[1].pos
|
||||||
self:go_to_pos(vector.new(curwp.x*0.7+nextwp.x*0.3,curwp.y,curwp.z*0.7+nextwp.z*0.3))
|
self:go_to_pos(vector.new(curwp.x*0.7+nextwp.x*0.3,curwp.y,curwp.z*0.7+nextwp.z*0.3), hurry)
|
||||||
return
|
return
|
||||||
end]]--
|
|
||||||
self:go_to_pos(self.current_target["pos"])
|
|
||||||
if self.current_target["action"] then
|
|
||||||
self:set_velocity(self.walk_velocity * 0.25)
|
|
||||||
end
|
end
|
||||||
|
self:go_to_pos(self.current_target.pos, hurry)
|
||||||
|
--if self.current_target["action"] then self:set_velocity(self.walk_velocity * 0.5) end
|
||||||
return
|
return
|
||||||
elseif self.current_target and self.current_target["pos"] then
|
elseif self.current_target and self.current_target.pos then
|
||||||
-- No waypoints left, but have current target and not close enough. Potentially last waypoint to go to.
|
-- No waypoints left, but have current target and not close enough. Potentially last waypoint to go to.
|
||||||
|
|
||||||
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
||||||
local failed_attempts = self.current_target["failed_attempts"]
|
local failed_attempts = self.current_target["failed_attempts"]
|
||||||
if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then
|
if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then
|
||||||
mcl_log("Failed to reach position " .. minetest.pos_to_string(self.current_target["pos"]) .. " too many times. At: "..minetest.pos_to_string(p).." Abandon route. Times tried: " .. failed_attempts .. " current distance "..distance_to_current_target)
|
mcl_log("Failed to reach position " .. minetest.pos_to_string(self.current_target.pos) .. " too many times. At: "..minetest.pos_to_string(p).." Abandon route. Times tried: " .. failed_attempts .. " current distance "..distance_to_current_target)
|
||||||
self.state = "stand"
|
self.state = "stand"
|
||||||
self.current_target = nil
|
self.current_target = nil
|
||||||
self.waypoints = nil
|
self.waypoints = nil
|
||||||
|
@ -412,9 +488,8 @@ function mob_class:check_gowp(dtime)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
--mcl_log("Not at pos with failed attempts ".. failed_attempts ..": ".. minetest.pos_to_string(p) .. "self.current_target: ".. minetest.pos_to_string(self.current_target["pos"]) .. ". Distance: ".. distance_to_current_target)
|
--mcl_log("Not at pos with failed attempts ".. failed_attempts ..": ".. minetest.pos_to_string(p) .. "self.current_target: ".. minetest.pos_to_string(self.current_target.pos) .. ". Distance: ".. distance_to_current_target)
|
||||||
self:go_to_pos(self.current_target["pos"])
|
self:go_to_pos(self.current_target["pos"])
|
||||||
self:turn_by((math.random() - 0.5), 2) -- but try turning left or right
|
|
||||||
-- Do i just delete current_target, and return so we can find final path.
|
-- Do i just delete current_target, and return so we can find final path.
|
||||||
else
|
else
|
||||||
-- Not at target, no current waypoints or current_target. Through the door and should be able to path to target.
|
-- Not at target, no current waypoints or current_target. Through the door and should be able to path to target.
|
||||||
|
@ -447,6 +522,7 @@ function mob_class:check_gowp(dtime)
|
||||||
|
|
||||||
-- I don't think we need the following anymore, but test first.
|
-- I don't think we need the following anymore, but test first.
|
||||||
-- Maybe just need something to path to target if no waypoints left
|
-- Maybe just need something to path to target if no waypoints left
|
||||||
|
--[[ ok, let's try
|
||||||
if self.current_target and self.current_target["pos"] and (self.waypoints and #self.waypoints == 0) then
|
if self.current_target and self.current_target["pos"] and (self.waypoints and #self.waypoints == 0) then
|
||||||
local updated_p = self.object:get_pos()
|
local updated_p = self.object:get_pos()
|
||||||
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
||||||
|
@ -465,4 +541,5 @@ function mob_class:check_gowp(dtime)
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,15 +28,22 @@ local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false
|
||||||
local mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48
|
local mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48
|
||||||
|
|
||||||
-- check if within physical map limits (-30911 to 30927)
|
-- check if within physical map limits (-30911 to 30927)
|
||||||
local map_min, map_max = -30912, 30928
|
local function within_limits(pos, radius)
|
||||||
if mcl_vars and mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
local wmin, wmax = -30912, 30928
|
||||||
map_min, map_max = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
if mcl_vars then
|
||||||
end
|
if mcl_vars.mapgen_edge_min and mcl_vars.mapgen_edge_max then
|
||||||
local function within_limits(pos)
|
wmin, wmax = mcl_vars.mapgen_edge_min, mcl_vars.mapgen_edge_max
|
||||||
return pos
|
end
|
||||||
and pos.x >= map_min and pos.x <= map_max
|
end
|
||||||
and pos.y >= map_min and pos.y <= map_max
|
if radius then
|
||||||
and pos.z >= map_min and pos.z <= map_max
|
wmin = wmin - radius
|
||||||
|
wmax = wmax + radius
|
||||||
|
end
|
||||||
|
if not pos then return true end
|
||||||
|
for _,v in pairs(pos) do
|
||||||
|
if v < wmin or v > wmax then return false end
|
||||||
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Function that update some helpful variables on the mobs position:
|
-- Function that update some helpful variables on the mobs position:
|
||||||
|
@ -222,7 +229,7 @@ end
|
||||||
-- @return target angle
|
-- @return target angle
|
||||||
function mob_class:turn_in_direction(dx, dz, delay, dtime)
|
function mob_class:turn_in_direction(dx, dz, delay, dtime)
|
||||||
if abs(dx) == 0 and abs(dz) == 0 then return self.object:get_yaw() + self.rotate end
|
if abs(dx) == 0 and abs(dz) == 0 then return self.object:get_yaw() + self.rotate end
|
||||||
return self:set_yaw(-atan2(dx, dz), delay, dtime)
|
return self:set_yaw(-atan2(dx, dz) - self.rotate, delay, dtime) + self.rotate
|
||||||
end
|
end
|
||||||
-- Absolute turn into a particular direction
|
-- Absolute turn into a particular direction
|
||||||
-- @param yaw number: angle in radians
|
-- @param yaw number: angle in radians
|
||||||
|
@ -306,7 +313,7 @@ function mob_class:flight_check()
|
||||||
if not self.standing_in or self.standing_in.name == "ignore" then return true end -- unknown?
|
if not self.standing_in or self.standing_in.name == "ignore" then return true end -- unknown?
|
||||||
if not self.fly_in then return false end
|
if not self.fly_in then return false end
|
||||||
local nod = self.standing_in.name
|
local nod = self.standing_in.name
|
||||||
-- todo: allow flowers etc. for birds
|
-- todo: allow flowers etc. for birds
|
||||||
for _,checknode in pairs(self.fly_in) do
|
for _,checknode in pairs(self.fly_in) do
|
||||||
if nod == checknode then return true end
|
if nod == checknode then return true end
|
||||||
end
|
end
|
||||||
|
@ -315,15 +322,26 @@ end
|
||||||
|
|
||||||
-- check if mob is dead or only hurt
|
-- check if mob is dead or only hurt
|
||||||
function mob_class:check_for_death(cause, cmi_cause)
|
function mob_class:check_for_death(cause, cmi_cause)
|
||||||
if self.state == "die" then return true end
|
|
||||||
-- skip effects if unchanged
|
if self.state == "die" then
|
||||||
if self.health == self.old_health and self.health > 0 then return false end
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- has health actually changed?
|
||||||
|
if self.health == self.old_health and self.health > 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local damaged = self.health < self.old_health
|
local damaged = self.health < self.old_health
|
||||||
self.old_health = self.health
|
self.old_health = self.health
|
||||||
|
|
||||||
|
-- still got some health?
|
||||||
if self.health > 0 then
|
if self.health > 0 then
|
||||||
if self.health > self.hp_max then self.health = self.hp_max end
|
|
||||||
|
-- make sure health isn't higher than max
|
||||||
|
if self.health > self.hp_max then
|
||||||
|
self.health = self.hp_max
|
||||||
|
end
|
||||||
|
|
||||||
-- play damage sound if health was reduced and make mob flash red.
|
-- play damage sound if health was reduced and make mob flash red.
|
||||||
if damaged then
|
if damaged then
|
||||||
|
@ -341,35 +359,57 @@ function mob_class:check_for_death(cause, cmi_cause)
|
||||||
self:mob_sound("death")
|
self:mob_sound("death")
|
||||||
|
|
||||||
local function death_handle(self)
|
local function death_handle(self)
|
||||||
-- dropped cooked item if mob died in fire or lava, no XP
|
if cmi_cause and cmi_cause["type"] then
|
||||||
|
--minetest.log("cmi_cause: " .. tostring(cmi_cause["type"]))
|
||||||
|
end
|
||||||
|
--minetest.log("cause: " .. tostring(cause))
|
||||||
|
|
||||||
|
-- TODO other env damage shouldn't drop xp
|
||||||
|
-- "rain", "water", "drowning", "suffocation"
|
||||||
|
|
||||||
|
-- dropped cooked item if mob died in fire or lava
|
||||||
if cause == "lava" or cause == "fire" then
|
if cause == "lava" or cause == "fire" then
|
||||||
self:item_drop(true, 0)
|
self:item_drop(true, 0)
|
||||||
return
|
else
|
||||||
end
|
local wielditem = ItemStack()
|
||||||
if cause == "rain" or cause == "water" or cause == "drowning" or cause == "suffocation" then
|
if cause == "hit" then
|
||||||
self:item_drop(false, 0)
|
local puncher = cmi_cause.puncher
|
||||||
return
|
if puncher then
|
||||||
end
|
wielditem = puncher:get_wielded_item()
|
||||||
local wielditem = cause == "hit" and cmi_cause.puncher and cmi_cause.puncher:get_wielded_item()
|
end
|
||||||
local cooked = mcl_burning.is_burning(self.object) or (wielditem and mcl_enchanting.has_enchantment(wielditem, "fire_aspect"))
|
end
|
||||||
local looting = wielditem and mcl_enchanting.get_enchantment(wielditem, "looting")
|
local cooked = mcl_burning.is_burning(self.object) or mcl_enchanting.has_enchantment(wielditem, "fire_aspect")
|
||||||
self:item_drop(cooked, looting)
|
local looting = mcl_enchanting.get_enchantment(wielditem, "looting")
|
||||||
|
self:item_drop(cooked, looting)
|
||||||
|
|
||||||
if (not self.child or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= math.huge) then
|
if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= math.huge) then
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local xp_amount = random(self.xp_min, self.xp_max)
|
local xp_amount = random(self.xp_min, self.xp_max)
|
||||||
if not mcl_sculk.handle_death(pos, xp_amount) then
|
|
||||||
if minetest.is_creative_enabled("") ~= true then
|
if not mcl_sculk.handle_death(pos, xp_amount) then
|
||||||
mcl_experience.throw_xp(pos, xp_amount)
|
--minetest.log("Xp not thrown")
|
||||||
|
if minetest.is_creative_enabled("") ~= true then
|
||||||
|
mcl_experience.throw_xp(pos, xp_amount)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--minetest.log("xp thrown")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- execute custom death function
|
-- execute custom death function
|
||||||
if self.on_die then
|
if self.on_die then
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if self.on_die(self, pos, cmi_cause) then
|
local on_die_exit = self.on_die(self, pos, cmi_cause)
|
||||||
|
if on_die_exit ~= true then
|
||||||
|
death_handle(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
if on_die_exit == true then
|
||||||
self.state = "die"
|
self.state = "die"
|
||||||
mcl_burning.extinguish(self.object)
|
mcl_burning.extinguish(self.object)
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
|
@ -382,6 +422,12 @@ function mob_class:check_for_death(cause, cmi_cause)
|
||||||
self.jockey = nil
|
self.jockey = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local collisionbox
|
||||||
|
if self.collisionbox then
|
||||||
|
collisionbox = table.copy(self.collisionbox)
|
||||||
|
end
|
||||||
|
|
||||||
self.state = "die"
|
self.state = "die"
|
||||||
self.attack = nil
|
self.attack = nil
|
||||||
self.v_start = false
|
self.v_start = false
|
||||||
|
@ -406,15 +452,18 @@ function mob_class:check_for_death(cause, cmi_cause)
|
||||||
local frames = self.animation.die_end - self.animation.die_start
|
local frames = self.animation.die_end - self.animation.die_start
|
||||||
local speed = self.animation.die_speed or 15
|
local speed = self.animation.die_speed or 15
|
||||||
length = max(frames / speed, 0) + DEATH_DELAY
|
length = max(frames / speed, 0) + DEATH_DELAY
|
||||||
self:set_animation("die")
|
self:set_animation( "die")
|
||||||
else
|
else
|
||||||
length = 1 + DEATH_DELAY
|
length = 1 + DEATH_DELAY
|
||||||
self:set_animation("stand", true)
|
self:set_animation( "stand", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Remove body after a few seconds and drop stuff
|
-- Remove body after a few seconds and drop stuff
|
||||||
local kill = function(self)
|
local kill = function(self)
|
||||||
if not self.object:get_luaentity() then return end
|
if not self.object:get_luaentity() then
|
||||||
|
return
|
||||||
|
end
|
||||||
death_handle(self)
|
death_handle(self)
|
||||||
local dpos = self.object:get_pos()
|
local dpos = self.object:get_pos()
|
||||||
local cbox = self.collisionbox
|
local cbox = self.collisionbox
|
||||||
|
@ -429,6 +478,7 @@ function mob_class:check_for_death(cause, cmi_cause)
|
||||||
else
|
else
|
||||||
minetest.after(length, kill, self)
|
minetest.after(length, kill, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -443,12 +493,15 @@ end
|
||||||
|
|
||||||
-- Deal light damage to mob, returns true if mob died
|
-- Deal light damage to mob, returns true if mob died
|
||||||
function mob_class:deal_light_damage(pos, damage)
|
function mob_class:deal_light_damage(pos, damage)
|
||||||
-- not during rain or snow
|
if not ((mcl_weather.rain.raining or mcl_weather.state == "snow") and mcl_weather.is_outdoor(pos)) then
|
||||||
if (mcl_weather.rain.raining or mcl_weather.state == "snow") and mcl_weather.is_outdoor(pos) then return false end
|
self.health = self.health - damage
|
||||||
|
|
||||||
self.health = self.health - damage
|
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
|
||||||
mcl_mobs.effect(pos, 5, "mcl_particles_smoke.png")
|
|
||||||
return self:check_for_death("light", {type = "light"})
|
if self:check_for_death("light", {type = "light"}) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- environmental damage (water, lava, fire, light etc.)
|
-- environmental damage (water, lava, fire, light etc.)
|
||||||
|
@ -457,27 +510,49 @@ function mob_class:do_env_damage()
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not pos then return end
|
if not pos then return end
|
||||||
|
|
||||||
|
self.time_of_day = minetest.get_timeofday()
|
||||||
|
|
||||||
-- remove mob if beyond map limits
|
-- remove mob if beyond map limits
|
||||||
if not within_limits(pos) then
|
if not within_limits(pos, 0) then
|
||||||
mcl_burning.extinguish(self.object)
|
mcl_burning.extinguish(self.object)
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- sunlight damage
|
local node = minetest.get_node(pos)
|
||||||
if (self.light_damage > 0 or self.sunlight_damage > 0 or self.ignited_by_sunlight) and pos.y >= mcl_vars.mg_overworld_min then
|
if node then
|
||||||
local sunlight = mcl_util.get_natural_light(pos) or 0
|
if node.name ~= "ignore" then
|
||||||
if self.light_damage > 0 and sunlight > 12 then
|
-- put below code in this block if we can prove that unloaded maps are causing crash.
|
||||||
if self:deal_light_damage(pos, self.light_damage) then return true end
|
-- it should warn then error
|
||||||
|
else
|
||||||
|
--minetest.log("warning", "Pos is ignored: " .. dump(pos))
|
||||||
end
|
end
|
||||||
if sunlight >= minetest.LIGHT_MAX and (self.sunlight_damage > 0 or self.ignited_by_sunlight) then
|
|
||||||
if not self.armor_list or not self.armor_list.helmet or (self.armor_list.helmet and self.armor_list.helmet == "") then
|
local sunlight = mcl_util.get_natural_light(pos, self.time_of_day)
|
||||||
if self.ignited_by_sunlight then
|
|
||||||
mcl_burning.set_on_fire(self.object, 10)
|
if self.light_damage ~= 0 and (sunlight or 0) > 12 then
|
||||||
end
|
if self:deal_light_damage(pos, self.light_damage) then
|
||||||
if self.sunlight_damage > 0 and self:deal_light_damage(pos, self.sunlight_damage) then return true end
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local _, dim = mcl_worlds.y_to_layer(pos.y)
|
||||||
|
if (self.sunlight_damage ~= 0 or self.ignited_by_sunlight) and (sunlight or 0) >= minetest.LIGHT_MAX and dim == "overworld" then
|
||||||
|
if self.armor_list and not self.armor_list.helmet or not self.armor_list or self.armor_list and self.armor_list.helmet and self.armor_list.helmet == "" then
|
||||||
|
if self.ignited_by_sunlight then
|
||||||
|
mcl_burning.set_on_fire(self.object, 10)
|
||||||
|
else
|
||||||
|
self:deal_light_damage(pos, self.sunlight_damage)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
local y_level = self.collisionbox[2]
|
||||||
|
|
||||||
|
if self.child then
|
||||||
|
y_level = self.collisionbox[2] * 0.5
|
||||||
end
|
end
|
||||||
|
|
||||||
local standin = self.standing_in
|
local standin = self.standing_in
|
||||||
|
@ -486,6 +561,10 @@ function mob_class:do_env_damage()
|
||||||
mcl_potions.give_effect_by_level("withering", self.object, 2, 2)
|
mcl_potions.give_effect_by_level("withering", self.object, 2, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local nodef = minetest.registered_nodes[self.standing_in]
|
||||||
|
local nodef2 = minetest.registered_nodes[self.standing_on]
|
||||||
|
local nodef3 = minetest.registered_nodes[self.standing_under]
|
||||||
|
|
||||||
-- rain
|
-- rain
|
||||||
if self.rain_damage > 0 and mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
|
if self.rain_damage > 0 and mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
|
||||||
self.health = self.health - self.rain_damage
|
self.health = self.health - self.rain_damage
|
||||||
|
@ -517,7 +596,7 @@ function mob_class:do_env_damage()
|
||||||
end
|
end
|
||||||
if self.fire_damage > 0 and standin.groups.fire then
|
if self.fire_damage > 0 and standin.groups.fire then
|
||||||
self.health = self.health - self.fire_damage
|
self.health = self.health - self.fire_damage
|
||||||
mcl_mobs.effect(vector.offset(pos, 0, 1, 0), 5, "fire_basic_flame.png", nil, nil, 1, nil)
|
mcl_mobs.effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil)
|
||||||
mcl_burning.set_on_fire(self.object, 5)
|
mcl_burning.set_on_fire(self.object, 5)
|
||||||
if self:check_for_death("fire", {type = "environment", pos = pos, node = standin.name}) then
|
if self:check_for_death("fire", {type = "environment", pos = pos, node = standin.name}) then
|
||||||
return true
|
return true
|
||||||
|
@ -575,6 +654,7 @@ function mob_class:do_env_damage()
|
||||||
-- Drowning damage
|
-- Drowning damage
|
||||||
if self.breath_max ~= -1 then
|
if self.breath_max ~= -1 then
|
||||||
local drowning = false
|
local drowning = false
|
||||||
|
|
||||||
if self.breathes_in_water then
|
if self.breathes_in_water then
|
||||||
if not standin.groups.water then drowning = true end
|
if not standin.groups.water then drowning = true end
|
||||||
elseif standin.drowning > 0 and self.standing_under.drowning > 0 then
|
elseif standin.drowning > 0 and self.standing_under.drowning > 0 then
|
||||||
|
@ -583,9 +663,7 @@ function mob_class:do_env_damage()
|
||||||
|
|
||||||
if drowning then
|
if drowning then
|
||||||
self.breath = max(0, self.breath - 1)
|
self.breath = max(0, self.breath - 1)
|
||||||
if not self.breathes_in_water then
|
mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil)
|
||||||
mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil)
|
|
||||||
end
|
|
||||||
if self.breath <= 0 then
|
if self.breath <= 0 then
|
||||||
local dmg = standin.drowning > 0 and standin.drowning or 4
|
local dmg = standin.drowning > 0 and standin.drowning or 4
|
||||||
self:damage_effect(dmg)
|
self:damage_effect(dmg)
|
||||||
|
@ -610,7 +688,7 @@ function mob_class:do_env_damage()
|
||||||
-- Short grace period before starting to take suffocation damage.
|
-- Short grace period before starting to take suffocation damage.
|
||||||
-- This is different from players, who take damage instantly.
|
-- This is different from players, who take damage instantly.
|
||||||
-- This has been done because mobs might briefly be inside solid nodes
|
-- This has been done because mobs might briefly be inside solid nodes
|
||||||
-- when, e.g., climbing up stairs.
|
-- when e.g. climbing up stairs.
|
||||||
-- This is a bit hacky because it assumes that do_env_damage
|
-- This is a bit hacky because it assumes that do_env_damage
|
||||||
-- is called roughly every second only.
|
-- is called roughly every second only.
|
||||||
self.suffocation_timer = self.suffocation_timer + 1
|
self.suffocation_timer = self.suffocation_timer + 1
|
||||||
|
@ -630,7 +708,7 @@ function mob_class:do_env_damage()
|
||||||
return self:check_for_death("unknown", {type = "unknown"})
|
return self:check_for_death("unknown", {type = "unknown"})
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:step_damage(dtime, pos)
|
function mob_class:step_damage (dtime, pos)
|
||||||
if not self.fire_resistant then
|
if not self.fire_resistant then
|
||||||
mcl_burning.tick(self.object, dtime, self)
|
mcl_burning.tick(self.object, dtime, self)
|
||||||
if not self.object:get_pos() then return true end -- mcl_burning.tick may remove object immediately
|
if not self.object:get_pos() then return true end -- mcl_burning.tick may remove object immediately
|
||||||
|
@ -829,6 +907,7 @@ function mob_class:check_water_flow(dtime, pos)
|
||||||
local def = self.standing_in
|
local def = self.standing_in
|
||||||
-- Move item around on flowing liquids
|
-- Move item around on flowing liquids
|
||||||
if def and def.liquidtype == "flowing" then
|
if def and def.liquidtype == "flowing" then
|
||||||
|
|
||||||
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
||||||
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
||||||
Luckily, this is exactly what we need if we only care about water, which has this flowing distance. ]]
|
Luckily, this is exactly what we need if we only care about water, which has this flowing distance. ]]
|
||||||
|
@ -874,4 +953,3 @@ function mob_class:check_suspend(player_in_active_range)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ local axolotl = {
|
||||||
fly = true,
|
fly = true,
|
||||||
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
fly_in = { "mcl_core:water_source", "mclx_core:river_water_source" },
|
||||||
breathes_in_water = true,
|
breathes_in_water = true,
|
||||||
jump = false, -- disable for now, because they will not find back
|
jump = false, -- would get them out of the water too often
|
||||||
damage = 2,
|
damage = 2,
|
||||||
reach = 2,
|
reach = 2,
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
|
@ -82,7 +82,13 @@ local axolotl = {
|
||||||
"mobs_mc:glow_squid",
|
"mobs_mc:glow_squid",
|
||||||
"mobs_mc:salmon",
|
"mobs_mc:salmon",
|
||||||
"mobs_mc:tropical_fish",
|
"mobs_mc:tropical_fish",
|
||||||
"mobs_mc:squid"
|
"mobs_mc:squid",
|
||||||
|
"mobs_mc:zombie", -- todo: only drowned?
|
||||||
|
"mobs_mc:baby_zombie",
|
||||||
|
"mobs_mc:husk",
|
||||||
|
"mobs_mc:baby_husk",
|
||||||
|
"mobs_mc:guardian_elder",
|
||||||
|
"mobs_mc:guardian",
|
||||||
},
|
},
|
||||||
runaway = true,
|
runaway = true,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
|
-- TODO: sounds
|
||||||
|
-- TODO: carry one item, spawn with item
|
||||||
|
-- TODO: add sleeping behavior
|
||||||
|
-- TODO: snow color depending on biome not randomly
|
||||||
|
-- TODO: pouncing - jump to attack behavior
|
||||||
|
-- TODO: use totem of undying when carried
|
||||||
|
|
||||||
|
-- Fox
|
||||||
|
local fox = {
|
||||||
|
description = S("Fox"),
|
||||||
|
type = "animal",
|
||||||
|
spawn_class = "passive",
|
||||||
|
can_despawn = true,
|
||||||
|
hp_min = 10,
|
||||||
|
hp_max = 10,
|
||||||
|
xp_min = 1,
|
||||||
|
xp_max = 3,
|
||||||
|
passive = false,
|
||||||
|
group_attack = false,
|
||||||
|
spawn_in_group = 4,
|
||||||
|
collisionbox = { -0.3, 0, -0.3, 0.3, 0.7, 0.3 },
|
||||||
|
visual = "mesh",
|
||||||
|
mesh = "mobs_mc_fox.b3d",
|
||||||
|
textures = {
|
||||||
|
{ "mobs_mc_fox.png", "mobs_mc_fox_sleep.png" },
|
||||||
|
{ "mobs_mc_snow_fox.png", "mobs_mc_snow_fox_sleep.png" }
|
||||||
|
},
|
||||||
|
makes_footstep_sound = true,
|
||||||
|
head_swivel = "Bone.001",
|
||||||
|
bone_eye_height = 0.5,
|
||||||
|
head_eye_height = 0.1,
|
||||||
|
horizontal_head_height = 0,
|
||||||
|
curiosity = 5,
|
||||||
|
head_yaw = "z",
|
||||||
|
sounds = { }, -- FIXME
|
||||||
|
pathfinding = 1,
|
||||||
|
floats = 1,
|
||||||
|
view_range = 16,
|
||||||
|
walk_chance = 50,
|
||||||
|
walk_velocity = 2,
|
||||||
|
run_velocity = 3,
|
||||||
|
damage = 2,
|
||||||
|
reach = 1,
|
||||||
|
attack_type = "dogfight",
|
||||||
|
fear_height = 5,
|
||||||
|
-- drops = { }, -- TODO: only what they are carrying
|
||||||
|
follow = { "mcl_farming:sweet_berry" }, -- TODO: and glow berries, taming
|
||||||
|
animation = {
|
||||||
|
stand_start = 1, stand_end = 20, stand_speed = 20,
|
||||||
|
walk_start = 120, walk_end = 160, walk_speed = 80,
|
||||||
|
run_start = 160, run_end = 199, run_speed = 80,
|
||||||
|
punch_start = 80, punch_end = 105, punch_speed = 80,
|
||||||
|
sit_start = 30 , sit_end = 50,
|
||||||
|
sleep_start = 55, sleep_end = 75,
|
||||||
|
--wiggle_start = 170, wiggle_end = 230,
|
||||||
|
--die_start = 0, die_end = 0, die_speed = 0,--die_loop = 0,
|
||||||
|
},
|
||||||
|
jump = true,
|
||||||
|
jump_height = 2,
|
||||||
|
attacks_monsters = true,
|
||||||
|
attack_animals = true,
|
||||||
|
specific_attack = {
|
||||||
|
"mobs_mc:chicken", "mobs_mc:rabbit",
|
||||||
|
"mobs_mc:cod", "mobs_mc:salmon", "mobs_mc:tropical_fish"
|
||||||
|
-- TODO: baby turtles, monsters?
|
||||||
|
},
|
||||||
|
runaway_from = {
|
||||||
|
-- they are too cute for this: "player",
|
||||||
|
"mobs_mc:wolf",
|
||||||
|
-- TODO: and polar bear
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_mobs.register_mob("mobs_mc:fox", fox)
|
||||||
|
-- Spawn
|
||||||
|
mcl_mobs:spawn_specific(
|
||||||
|
"mobs_mc:fox",
|
||||||
|
"overworld",
|
||||||
|
"ground",
|
||||||
|
{
|
||||||
|
"Taiga",
|
||||||
|
"Taiga_beach",
|
||||||
|
"MegaTaiga",
|
||||||
|
"MegaSpruceTaiga",
|
||||||
|
"ColdTaiga",
|
||||||
|
"ColdTaiga_beach",
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
minetest.LIGHT_MAX+1,
|
||||||
|
30,
|
||||||
|
80,
|
||||||
|
7,
|
||||||
|
mobs_mc.water_level+3,
|
||||||
|
mcl_vars.mg_overworld_max)
|
||||||
|
|
||||||
|
|
||||||
|
mcl_mobs.register_egg("mobs_mc:fox", "Fox", "#ba9f8b", "#9f5219", 0)
|
|
@ -149,6 +149,7 @@ dofile(path .. "/salmon.lua")
|
||||||
dofile(path .. "/tropical_fish.lua")
|
dofile(path .. "/tropical_fish.lua")
|
||||||
dofile(path .. "/dolphin.lua")
|
dofile(path .. "/dolphin.lua")
|
||||||
|
|
||||||
|
dofile(path .. "/fox.lua") -- Mesh and animation by https://codeberg.org/pixelzone texture https://github.com/NovaWostra/Pixel-Perfection-Chorus-Eddit
|
||||||
|
|
||||||
dofile(path .. "/glow_squid.lua")
|
dofile(path .. "/glow_squid.lua")
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -952,6 +952,9 @@ local function go_home(entity, sleep)
|
||||||
entity.order = nil
|
entity.order = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
-- in case pathfinding fails, turn into the right direction anyways
|
||||||
|
local p = entity.object:get_pos()
|
||||||
|
entity:turn_in_direction(b.x - p.x, b.z - p.z, 8)
|
||||||
|
|
||||||
entity:gopath(b,function(entity,b)
|
entity:gopath(b,function(entity,b)
|
||||||
local b = entity._bed
|
local b = entity._bed
|
||||||
|
@ -1331,7 +1334,7 @@ local function do_work (self)
|
||||||
--mcl_log("Jobsite not valid")
|
--mcl_log("Jobsite not valid")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if vector.distance(self.object:get_pos(),self._jobsite) < 2 then
|
if vector.distance(self.object:get_pos(),self._jobsite) < 1.5 then
|
||||||
--mcl_log("Made it to work ok callback!")
|
--mcl_log("Made it to work ok callback!")
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
|
@ -2316,8 +2319,8 @@ mcl_mobs.register_mob("mobs_mc:villager", {
|
||||||
|
|
||||||
if cmi_cause and cmi_cause.puncher then
|
if cmi_cause and cmi_cause.puncher then
|
||||||
local l = cmi_cause.puncher:get_luaentity()
|
local l = cmi_cause.puncher:get_luaentity()
|
||||||
if l and math.random(2) == 1 and (l.name == "mobs_mc:zombie" or l.name == "mobs_mc:baby_zombie" or l.name == "mobs_mc:villager_zombie" or l.name == "mobs_mc:husk") then
|
if l and math.random(2) == 1 and( l.name == "mobs_mc:zombie" or l.name == "mobs_mc:baby_zombie" or l.name == "mobs_mc:villager_zombie" or l.name == "mobs_mc:husk") then
|
||||||
mcl_util.replace_mob(self.object, "mobs_mc:villager_zombie")
|
mcl_util.replace_mob(self.object,"mobs_mc:villager_zombie")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -157,6 +157,7 @@ function mcl_weather.rain.clear()
|
||||||
mcl_weather.rain.remove_sound(player)
|
mcl_weather.rain.remove_sound(player)
|
||||||
mcl_weather.rain.remove_player(player)
|
mcl_weather.rain.remove_player(player)
|
||||||
mcl_weather.remove_spawners_player(player)
|
mcl_weather.remove_spawners_player(player)
|
||||||
|
player:set_clouds({color="#FFF0EF"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
local modname = minetest.get_current_modname()
|
local modname = minetest.get_current_modname()
|
||||||
local modpath = minetest.get_modpath(modname)
|
local modpath = minetest.get_modpath(modname)
|
||||||
local NIGHT_VISION_RATIO = 0.45
|
local NIGHT_VISION_RATIO = 0.45
|
||||||
|
local DEBUG = false
|
||||||
|
|
||||||
-- Settings
|
-- Settings
|
||||||
local minimum_update_interval = { 250e3 }
|
local minimum_update_interval = { 250e3 }
|
||||||
|
@ -190,8 +191,8 @@ end
|
||||||
|
|
||||||
function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
||||||
-- Clamp current_val to valid range
|
-- Clamp current_val to valid range
|
||||||
current_val = math.min(minval, current_val)
|
current_val = math.max(minval, current_val)
|
||||||
current_val = math.max(maxval, current_val)
|
current_val = math.min(maxval, current_val)
|
||||||
|
|
||||||
-- Rescale current_val from a number between minval and maxval to a number between 1 and #colors
|
-- Rescale current_val from a number between minval and maxval to a number between 1 and #colors
|
||||||
local scaled_value = (current_val - minval) / (maxval - minval) * (#colors - 1) + 1.0
|
local scaled_value = (current_val - minval) / (maxval - minval) * (#colors - 1) + 1.0
|
||||||
|
@ -199,7 +200,7 @@ function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
||||||
-- Get the first color's values
|
-- Get the first color's values
|
||||||
local index1 = math.floor(scaled_value)
|
local index1 = math.floor(scaled_value)
|
||||||
local color1 = colors[index1]
|
local color1 = colors[index1]
|
||||||
local frac1 = scaled_value - index1
|
local frac1 = 1.0 - (scaled_value - index1)
|
||||||
|
|
||||||
-- Get the second color's values
|
-- Get the second color's values
|
||||||
local index2 = math.min(index1 + 1, #colors) -- clamp to maximum color index (will occur if index1 == #colors)
|
local index2 = math.min(index1 + 1, #colors) -- clamp to maximum color index (will occur if index1 == #colors)
|
||||||
|
@ -207,11 +208,32 @@ function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors)
|
||||||
local color2 = colors[index2]
|
local color2 = colors[index2]
|
||||||
|
|
||||||
-- Interpolate between color1 and color2
|
-- Interpolate between color1 and color2
|
||||||
return {
|
local res = {
|
||||||
r = math.floor(frac1 * color1.r + frac2 * color2.r),
|
r = math.floor(frac1 * color1.r + frac2 * color2.r),
|
||||||
g = math.floor(frac1 * color1.g + frac2 * color2.g),
|
g = math.floor(frac1 * color1.g + frac2 * color2.g),
|
||||||
b = math.floor(frac1 * color1.b + frac2 * color2.b),
|
b = math.floor(frac1 * color1.b + frac2 * color2.b),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if DEBUG then
|
||||||
|
minetest.log(dump({
|
||||||
|
minval = minval,
|
||||||
|
maxval = maxval,
|
||||||
|
current_val = current_val,
|
||||||
|
colors = colors,
|
||||||
|
res = res,
|
||||||
|
scaled_value = scaled_value,
|
||||||
|
|
||||||
|
frac1 = frac1,
|
||||||
|
index1 = index1,
|
||||||
|
color1 = color1,
|
||||||
|
|
||||||
|
frac2 = frac2,
|
||||||
|
index2 = index2,
|
||||||
|
color2 = color2,
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
return res
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Simple getter. Either returns user given players list or get all connected players if none provided
|
-- Simple getter. Either returns user given players list or get all connected players if none provided
|
||||||
|
|
|
@ -40,18 +40,21 @@ function dimension_handlers.overworld(player, sky_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Use overworld defaults
|
-- Use overworld defaults
|
||||||
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.15)
|
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
|
||||||
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27)
|
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27)
|
||||||
local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1)
|
local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1)
|
||||||
sky_data.sky = {
|
sky_data.sky = {
|
||||||
type = "regular",
|
type = "regular",
|
||||||
sky_color = {
|
sky_color = {
|
||||||
day_sky = day_color,
|
day_sky = day_color or "#7BA4FF",
|
||||||
day_horizon = day_color,
|
day_horizon = day_color or "#C0D8FF",
|
||||||
dawn_sky = dawn_color,
|
dawn_sky = dawn_color or "7BA4FF",
|
||||||
dawn_horizon = dawn_color,
|
dawn_horizon = dawn_color or "#C0D8FF",
|
||||||
night_sky = night_color,
|
night_sky = night_color or "000000",
|
||||||
night_horizon = night_color,
|
night_horizon = night_color or "4A6790",
|
||||||
|
fog_sun_tint = "#ff5f33",
|
||||||
|
fog_moon_tint = nil,
|
||||||
|
fog_tint_type = "custom",
|
||||||
},
|
},
|
||||||
clouds = true,
|
clouds = true,
|
||||||
}
|
}
|
||||||
|
@ -75,18 +78,15 @@ function dimension_handlers.overworld(player, sky_data)
|
||||||
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
|
local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5)
|
||||||
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75)
|
local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75)
|
||||||
local night_color = mcl_weather.skycolor.get_sky_layer_color(0)
|
local night_color = mcl_weather.skycolor.get_sky_layer_color(0)
|
||||||
sky_data.sky = {
|
table.update(sky_data.sky.sky_color,{
|
||||||
type = "regular",
|
day_sky = day_color or "#7BA4FF",
|
||||||
sky_color = {
|
day_horizon = day_color or "#C0D8FF",
|
||||||
day_sky = day_color,
|
dawn_sky = dawn_color or "7BA4FF",
|
||||||
day_horizon = day_color,
|
dawn_horizon = dawn_color or "#C0D8FF",
|
||||||
dawn_sky = dawn_color,
|
night_sky = night_color or "000000",
|
||||||
dawn_horizon = dawn_color,
|
night_horizon = night_color or "4A6790",
|
||||||
night_sky = night_color,
|
fog_tint_type = "default",
|
||||||
night_horizon = night_color,
|
})
|
||||||
},
|
|
||||||
clouds = true,
|
|
||||||
}
|
|
||||||
sky_data.sun = {visible = false, sunrise_visible = false}
|
sky_data.sun = {visible = false, sunrise_visible = false}
|
||||||
sky_data.moon = {visible = false}
|
sky_data.moon = {visible = false}
|
||||||
sky_data.stars = {visible = false}
|
sky_data.stars = {visible = false}
|
||||||
|
@ -164,7 +164,8 @@ function dimension_handlers.nether(player, sky_data)
|
||||||
end
|
end
|
||||||
|
|
||||||
function dimension_handlers.void(player, sky_data)
|
function dimension_handlers.void(player, sky_data)
|
||||||
sky_data.sky = { type = "plain",
|
sky_data.sky = {
|
||||||
|
type = "plain",
|
||||||
base_color = "#000000",
|
base_color = "#000000",
|
||||||
clouds = false,
|
clouds = false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,13 +75,15 @@ function mcl_weather.has_snow(pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_weather.snow.set_sky_box()
|
function mcl_weather.snow.set_sky_box()
|
||||||
mcl_weather.skycolor.add_layer(
|
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-snow-sky" then
|
||||||
"weather-pack-snow-sky",
|
mcl_weather.skycolor.add_layer(
|
||||||
{{r=0, g=0, b=0},
|
"weather-pack-snow-sky",
|
||||||
{r=85, g=86, b=86},
|
{{r=0, g=0, b=0},
|
||||||
{r=135, g=135, b=135},
|
{r=85, g=86, b=86},
|
||||||
{r=85, g=86, b=86},
|
{r=135, g=135, b=135},
|
||||||
{r=0, g=0, b=0}})
|
{r=85, g=86, b=86},
|
||||||
|
{r=0, g=0, b=0}})
|
||||||
|
end
|
||||||
mcl_weather.skycolor.active = true
|
mcl_weather.skycolor.active = true
|
||||||
for _, player in pairs(get_connected_players()) do
|
for _, player in pairs(get_connected_players()) do
|
||||||
player:set_clouds({color="#ADADADE8"})
|
player:set_clouds({color="#ADADADE8"})
|
||||||
|
|
|
@ -23,13 +23,15 @@ minetest.register_globalstep(function(dtime)
|
||||||
mcl_weather.rain.make_weather()
|
mcl_weather.rain.make_weather()
|
||||||
|
|
||||||
if mcl_weather.thunder.init_done == false then
|
if mcl_weather.thunder.init_done == false then
|
||||||
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
|
if mcl_weather.skycolor.current_layer_name() ~= "weather-pack-thunder-sky" then
|
||||||
{r=0, g=0, b=0},
|
mcl_weather.skycolor.add_layer("weather-pack-thunder-sky", {
|
||||||
{r=40, g=40, b=40},
|
{r=0, g=0, b=0},
|
||||||
{r=85, g=86, b=86},
|
{r=40, g=40, b=40},
|
||||||
{r=40, g=40, b=40},
|
{r=85, g=86, b=86},
|
||||||
{r=0, g=0, b=0},
|
{r=40, g=40, b=40},
|
||||||
})
|
{r=0, g=0, b=0},
|
||||||
|
})
|
||||||
|
end
|
||||||
mcl_weather.skycolor.active = true
|
mcl_weather.skycolor.active = true
|
||||||
for _, player in pairs(get_connected_players()) do
|
for _, player in pairs(get_connected_players()) do
|
||||||
player:set_clouds({color="#3D3D3FE8"})
|
player:set_clouds({color="#3D3D3FE8"})
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
|
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
|
||||||
|
|
||||||
-- CONSTS
|
-- CONSTS
|
||||||
local DOUBLE_DROP_CHANCE = 8
|
|
||||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
|
||||||
-- "BAMBOO" goes here.
|
|
||||||
local BAMBOO = "mcl_bamboo:bamboo"
|
local BAMBOO = "mcl_bamboo:bamboo"
|
||||||
local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap"
|
local BAMBOO_ENDCAP_NAME = "mcl_bamboo:bamboo_endcap"
|
||||||
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||||
|
@ -16,7 +13,7 @@ local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||||
local modname = minetest.get_current_modname()
|
local modname = minetest.get_current_modname()
|
||||||
local S = minetest.get_translator(modname)
|
local S = minetest.get_translator(modname)
|
||||||
local node_sound = mcl_sounds.node_sound_wood_defaults()
|
local node_sound = mcl_sounds.node_sound_wood_defaults()
|
||||||
local pr = PseudoRandom((os.time() + 15766) * 12) -- switched from math.random() to PseudoRandom because the random wasn't very random.
|
local pr = PseudoRandom((os.time() + 15766) * 12)
|
||||||
|
|
||||||
local on_rotate
|
local on_rotate
|
||||||
if minetest.get_modpath("screwdriver") then
|
if minetest.get_modpath("screwdriver") then
|
||||||
|
@ -31,33 +28,7 @@ local bamboo_def = {
|
||||||
paramtype = "light",
|
paramtype = "light",
|
||||||
groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3},
|
groups = {handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3},
|
||||||
sounds = node_sound,
|
sounds = node_sound,
|
||||||
|
drop = BAMBOO,
|
||||||
drop = {
|
|
||||||
max_items = 1,
|
|
||||||
-- From the API:
|
|
||||||
-- max_items: Maximum number of item lists to drop.
|
|
||||||
-- The entries in 'items' are processed in order. For each:
|
|
||||||
-- Item filtering is applied, chance of drop is applied, if both are
|
|
||||||
-- successful the entire item list is dropped.
|
|
||||||
-- Entry processing continues until the number of dropped item lists
|
|
||||||
-- equals 'max_items'.
|
|
||||||
-- Therefore, entries should progress from low to high drop chance.
|
|
||||||
items = {
|
|
||||||
-- Examples:
|
|
||||||
{
|
|
||||||
-- 1 in DOUBLE_DROP_CHANCE chance of dropping.
|
|
||||||
-- Default rarity is '1'.
|
|
||||||
rarity = DOUBLE_DROP_CHANCE,
|
|
||||||
items = {BAMBOO .. " 2"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
-- 1 in 1 chance of dropping. (Note: this means that it will drop 100% of the time.)
|
|
||||||
-- Default rarity is '1'.
|
|
||||||
rarity = 1,
|
|
||||||
items = {BAMBOO},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
inventory_image = "mcl_bamboo_bamboo_shoot.png",
|
inventory_image = "mcl_bamboo_bamboo_shoot.png",
|
||||||
wield_image = "mcl_bamboo_bamboo_shoot.png",
|
wield_image = "mcl_bamboo_bamboo_shoot.png",
|
||||||
|
@ -86,7 +57,6 @@ local bamboo_def = {
|
||||||
on_rotate = on_rotate,
|
on_rotate = on_rotate,
|
||||||
|
|
||||||
on_place = function(itemstack, placer, pointed_thing)
|
on_place = function(itemstack, placer, pointed_thing)
|
||||||
|
|
||||||
if not pointed_thing then
|
if not pointed_thing then
|
||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
|
@ -241,9 +211,6 @@ local bamboo_def = {
|
||||||
if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then
|
if node_above and ((bamboo_node and bamboo_node > 0) or node_above.name == BAMBOO_ENDCAP_NAME) then
|
||||||
minetest.remove_node(new_pos)
|
minetest.remove_node(new_pos)
|
||||||
minetest.sound_play(node_sound.dug, sound_params, true)
|
minetest.sound_play(node_sound.dug, sound_params, true)
|
||||||
if pr:next(1, DOUBLE_DROP_CHANCE) == 1 then
|
|
||||||
minetest.add_item(new_pos, istack)
|
|
||||||
end
|
|
||||||
minetest.add_item(new_pos, istack)
|
minetest.add_item(new_pos, istack)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -9,8 +9,6 @@ local SIDE_SCAFFOLDING = false
|
||||||
local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal"
|
local SIDE_SCAFFOLD_NAME = "mcl_bamboo:scaffolding_horizontal"
|
||||||
-- ---------------------------------------------------------------------------
|
-- ---------------------------------------------------------------------------
|
||||||
local SCAFFOLDING_NAME = "mcl_bamboo:scaffolding"
|
local SCAFFOLDING_NAME = "mcl_bamboo:scaffolding"
|
||||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
|
||||||
-- "BAMBOO" goes here.
|
|
||||||
local BAMBOO = "mcl_bamboo:bamboo"
|
local BAMBOO = "mcl_bamboo:bamboo"
|
||||||
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
-- LOCALS
|
-- LOCALS
|
||||||
local modname = minetest.get_current_modname()
|
local modname = minetest.get_current_modname()
|
||||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
|
||||||
-- "BAMBOO" goes here.
|
|
||||||
local BAMBOO = "mcl_bamboo:bamboo"
|
local BAMBOO = "mcl_bamboo:bamboo"
|
||||||
|
|
||||||
mcl_bamboo = {}
|
mcl_bamboo = {}
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
--- These are all of the fuel recipes and all of the crafting recipes, consolidated into one place.
|
--- These are all of the fuel recipes and all of the crafting recipes, consolidated into one place.
|
||||||
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
|
--- Copyright (C) 2022 - 2023, Michieal. See License.txt
|
||||||
|
|
||||||
-- Used everywhere. Often this is just the name, but it makes sense to me as BAMBOO, because that's how I think of it...
|
|
||||||
-- "BAMBOO" goes here.
|
|
||||||
local BAMBOO = "mcl_bamboo:bamboo"
|
local BAMBOO = "mcl_bamboo:bamboo"
|
||||||
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
local BAMBOO_PLANK = BAMBOO .. "_plank"
|
||||||
-- Craftings
|
-- Craftings
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 649 B |
Binary file not shown.
After Width: | Height: | Size: 649 B |
Binary file not shown.
After Width: | Height: | Size: 602 B |
Binary file not shown.
After Width: | Height: | Size: 600 B |
Loading…
Reference in New Issue