Compare commits
128 Commits
73009bab64
...
6def6b85f8
Author | SHA1 | Date |
---|---|---|
kno10 | 6def6b85f8 | |
kno10 | a82c38abd0 | |
kno10 | 0f5a2a3802 | |
kno10 | c8c0f200cb | |
kno10 | 0984a7e53f | |
kno10 | d0a88adc20 | |
kno10 | 26210439b0 | |
kno10 | 3e214793aa | |
kno10 | 52f8f15063 | |
kno10 | 31251194d3 | |
kno10 | 305507d127 | |
kno10 | 7fb2a3aff4 | |
kno10 | fd1d32cbf8 | |
kno10 | d0fd68e9ba | |
kno10 | aa1de58701 | |
kno10 | 06c693f627 | |
kno10 | fdebdef12d | |
kno10 | c7f3c051d7 | |
kno10 | 06ba4487c6 | |
kno10 | 40492282ae | |
kno10 | 8ad5d16ccc | |
kno10 | d32d64250b | |
kno10 | 7693c9b02a | |
kno10 | 815e929ccb | |
kno10 | 0708c804d3 | |
kno10 | 0b381dce31 | |
kno10 | eb30e60dde | |
kno10 | ee3b14d4db | |
kno10 | 8da3587458 | |
kno10 | fdb05cee5d | |
kno10 | 44dc17fa60 | |
kno10 | 688fb13874 | |
kno10 | bad44084c6 | |
kno10 | 5d3d6470b8 | |
kno10 | fb3c85e289 | |
kno10 | f6f5481f30 | |
the-real-herowl | c428fa576b | |
teknomunk | a46833eaa4 | |
teknomunk | 3514fe211f | |
teknomunk | 94d9e4c881 | |
teknomunk | 6b1aa43238 | |
teknomunk | cfdef2435a | |
teknomunk | 49c8ae2fa0 | |
teknomunk | 6ada1a3477 | |
teknomunk | 189a2c62ad | |
teknomunk | 981cddddd4 | |
teknomunk | 66b5a369f1 | |
teknomunk | 4eda77acd1 | |
teknomunk | afc270195a | |
teknomunk | 8f53074b58 | |
teknomunk | 70e8ba9a89 | |
teknomunk | 6741c5a809 | |
teknomunk | d09791db7b | |
teknomunk | 354160e9e6 | |
teknomunk | cf1325d466 | |
teknomunk | 7112369917 | |
teknomunk | e6e13bdc67 | |
teknomunk | 42d37210c5 | |
teknomunk | c3a33ea2c2 | |
teknomunk | 7f6d456a32 | |
teknomunk | 44d154f594 | |
teknomunk | eb6131b037 | |
teknomunk | 3c2f2593db | |
teknomunk | 9e6d49dd38 | |
teknomunk | 4a865fa2df | |
teknomunk | 55b4d3d5ee | |
teknomunk | 09bcf3d22b | |
teknomunk | 57678e31bc | |
teknomunk | d5684ca305 | |
teknomunk | a4f1ccd0ee | |
teknomunk | 1e0f7618ba | |
teknomunk | f44102c238 | |
teknomunk | 5b1fcf76f6 | |
kabou | f61a7ab4cb | |
kabou | 4449f74742 | |
kabou | ba1e0e4301 | |
kabou | 7938fba4a5 | |
kabou | 8acddab74f | |
kabou | c2c7df820f | |
kabou | e5cf4bd225 | |
kabou | 810051c591 | |
kabou | ae56a864d0 | |
kabou | 7ddcf3f93f | |
kabou | e8d965e21a | |
kabou | 8855246dd4 | |
kabou | 3889abbaf4 | |
kabou | f6235e8e92 | |
kabou | 2190080832 | |
kabou | ea1d52baab | |
kabou | fdc7f4634d | |
kabou | bde0d9b238 | |
teknomunk | f644d37332 | |
kabou | 17f2d85de9 | |
kabou | d07e8d9536 | |
kabou | 5d2fa8072a | |
kabou | 2d8bb12fad | |
kabou | 69032c3222 | |
kabou | 71e6fa9646 | |
kabou | 9ea52ce9b3 | |
kabou | 0422635047 | |
kno10 | b540e6c77b | |
kno10 | d49426d453 | |
the-real-herowl | 2b7b7f1872 | |
kno10 | b5afa34469 | |
kno10 | ebf6cf32e8 | |
kno10 | a8318f6600 | |
kno10 | fa7a7f4e81 | |
kno10 | c097c65262 | |
kno10 | 220a7b06e6 | |
kno10 | 540a070c59 | |
kno10 | 78a958db4e | |
kno10 | e9453d6210 | |
kno10 | 9376cf92b1 | |
kno10 | c4030115c4 | |
kno10 | e1ace4ad01 | |
the-real-herowl | e3b7847df1 | |
Mikita Wiśniewski | f86a641dfa | |
Mikita Wiśniewski | 084741b733 | |
Mikita Wiśniewski | d5bc0613d8 | |
Loveaabb | f26c34e65f | |
Loveaabb | 04e29c5796 | |
Elias Åström | 45ae170447 | |
Elias Åström | d0d1217dec | |
Elias Åström | cffc8e0145 | |
the-real-herowl | b136cbf9bb | |
the-real-herowl | e6d8d840db | |
Mikita Wiśniewski | 78125f425a | |
cora | cb1999414b |
|
@ -1131,3 +1131,25 @@ if not vector.in_area then
|
|||
(pos.z >= min.z) and (pos.z <= max.z)
|
||||
end
|
||||
end
|
||||
|
||||
-- Traces along a line of nodes vertically to find the next possition that isn't an allowed node
|
||||
---@param pos The position to start tracing from
|
||||
---@param dir The direction to trace in. 1 is up, -1 is down, all other values are not allowed.
|
||||
---@param allowed_nodes A set of node names to trace along.
|
||||
---@param limit The maximum number of steps to make. Defaults to 16 if nil or missing
|
||||
---@return Three return values:
|
||||
--- the position of the next node that isn't allowed or nil if no such node was found,
|
||||
--- the distance from the start where that node was found,
|
||||
--- the node table if a node was found
|
||||
function mcl_util.trace_nodes(pos, dir, allowed_nodes, limit)
|
||||
if (dir ~= -1) and (dir ~= 1) then return nil, 0, nil end
|
||||
limit = limit or 16
|
||||
|
||||
for i = 1,limit do
|
||||
pos = vector.offset(pos, 0, dir, 0)
|
||||
local node = minetest.get_node(pos)
|
||||
if not allowed_nodes[node.name] then return pos, i, node end
|
||||
end
|
||||
|
||||
return nil, limit, nil
|
||||
end
|
||||
|
|
|
@ -1,29 +1,23 @@
|
|||
local mob_class = mcl_mobs.mob_class
|
||||
local mob_class_meta = {__index = mcl_mobs.mob_class}
|
||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
-- API for Mobs Redo: VoxeLibre Edition
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
local CRASH_WARN_FREQUENCY = 60
|
||||
local LIFETIMER_DISTANCE = 47
|
||||
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
|
||||
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90
|
||||
-- 30927 seems to be the edge of the world, so could be closer, but this is safer
|
||||
|
||||
-- Localize
|
||||
local S = minetest.get_translator("mcl_mobs")
|
||||
|
||||
local DEVELOPMENT = minetest.settings:get_bool("mcl_development",false)
|
||||
|
||||
-- Invisibility mod check
|
||||
mcl_mobs.invis = {}
|
||||
|
||||
local remove_far = true
|
||||
|
||||
local mobs_debug = minetest.settings:get_bool("mobs_debug", false) -- Shows helpful debug info above each mob
|
||||
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
|
||||
|
||||
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
|
||||
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90
|
||||
-- 30927 seems to be the edge of the world, so could be closer, but this is safer
|
||||
|
||||
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", true)
|
||||
local DEVELOPMENT = minetest.settings:get_bool("mcl_development", false)
|
||||
|
||||
-- Peaceful mode message so players will know there are no monsters
|
||||
if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
||||
|
@ -33,13 +27,21 @@ if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
|||
end)
|
||||
end
|
||||
|
||||
-- not used yet
|
||||
function mob_class:safe_remove()
|
||||
self._removed = true
|
||||
minetest.after(0,function(obj)
|
||||
if obj and obj:get_pos() then
|
||||
mcl_burning.extinguish(obj)
|
||||
obj:remove()
|
||||
end
|
||||
end,self.object)
|
||||
end
|
||||
|
||||
function mob_class:update_tag() --update nametag and/or the debug box
|
||||
local tag
|
||||
if mobs_debug then
|
||||
local name = self.name
|
||||
if self.nametag and self.nametag ~= "" then
|
||||
name = self.nametag
|
||||
end
|
||||
local name = self.nametag ~= "" and self.nametag or self.name
|
||||
tag = "name = '"..tostring(name).."'\n"..
|
||||
"state = '"..tostring(self.state).."'\n"..
|
||||
"order = '"..tostring(self.order).."'\n"..
|
||||
|
@ -56,9 +58,7 @@ function mob_class:update_tag() --update nametag and/or the debug box
|
|||
else
|
||||
tag = self.nametag
|
||||
end
|
||||
self.object:set_properties({
|
||||
nametag = tag,
|
||||
})
|
||||
self.object:set_properties({ nametag = tag })
|
||||
end
|
||||
|
||||
function mob_class:jock_to(mob, reletive_pos, rot)
|
||||
|
@ -74,19 +74,15 @@ function mob_class:jock_to(mob, reletive_pos, rot)
|
|||
end
|
||||
|
||||
function mob_class:get_staticdata()
|
||||
|
||||
for _,p in pairs(minetest.get_connected_players()) do
|
||||
self:remove_particlespawners(p:get_player_name())
|
||||
end
|
||||
|
||||
-- remove mob when out of range unless tamed
|
||||
if remove_far
|
||||
and self:despawn_allowed()
|
||||
and self.lifetimer <= 20 then
|
||||
if remove_far and self:despawn_allowed() and self.lifetimer <= 20 then
|
||||
if spawn_logging then
|
||||
minetest.log("action", "[mcl_mobs] Mob "..tostring(self.name).." despawns at "..minetest.pos_to_string(vector.round(self.object:get_pos())) .. " - out of range")
|
||||
end
|
||||
|
||||
return "remove"-- nil
|
||||
end
|
||||
|
||||
|
@ -95,17 +91,9 @@ function mob_class:get_staticdata()
|
|||
self.state = "stand"
|
||||
|
||||
local tmp = {}
|
||||
|
||||
for tag, stat in pairs(self) do
|
||||
|
||||
local t = type(stat)
|
||||
|
||||
if t ~= "function"
|
||||
and t ~= "nil"
|
||||
and t ~= "userdata"
|
||||
and tag ~= "_cmi_components" then
|
||||
tmp[tag] = self[tag]
|
||||
end
|
||||
if t ~= "function" and t ~= "nil" and t ~= "userdata" and tag ~= "_cmi_components" then tmp[tag] = self[tag] end
|
||||
end
|
||||
|
||||
tmp._mcl_potions = self._mcl_potions
|
||||
|
@ -120,10 +108,7 @@ function mob_class:get_staticdata()
|
|||
end
|
||||
|
||||
local function valid_texture(self, def_textures)
|
||||
if not self.base_texture then
|
||||
return false
|
||||
end
|
||||
|
||||
if not self.base_texture then return false end
|
||||
if self.texture_selected then
|
||||
if #def_textures < self.texture_selected then
|
||||
self.texture_selected = nil
|
||||
|
@ -136,44 +121,27 @@ end
|
|||
|
||||
function mob_class:mob_activate(staticdata, def, dtime)
|
||||
if not self.object:get_pos() or staticdata == "remove" then
|
||||
mcl_burning.extinguish(self.object)
|
||||
self.object:remove()
|
||||
self:safe_remove()
|
||||
return
|
||||
end
|
||||
if self.type == "monster"
|
||||
and minetest.settings:get_bool("only_peaceful_mobs", false) then
|
||||
mcl_burning.extinguish(self.object)
|
||||
self.object:remove()
|
||||
if self.type == "monster" and minetest.settings:get_bool("only_peaceful_mobs", false) then
|
||||
self:safe_remove()
|
||||
return
|
||||
end
|
||||
|
||||
local tmp = minetest.deserialize(staticdata)
|
||||
|
||||
if tmp then
|
||||
-- Patch incorrectly converted mobs
|
||||
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then
|
||||
mcl_mobs.strip_staticdata(tmp)
|
||||
end
|
||||
|
||||
for _,stat in pairs(tmp) do
|
||||
self[_] = stat
|
||||
end
|
||||
if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then mcl_mobs.strip_staticdata(tmp) end
|
||||
for _, stat in pairs(tmp) do self[_] = stat end
|
||||
end
|
||||
|
||||
--If textures in definition change, reload textures
|
||||
if not valid_texture(self, def.textures) then
|
||||
|
||||
-- compatiblity with old simple mobs textures
|
||||
if type(def.textures[1]) == "string" then
|
||||
def.textures = {def.textures}
|
||||
end
|
||||
|
||||
if not self.texture_selected then
|
||||
local c = 1
|
||||
if #def.textures > c then c = #def.textures end
|
||||
self.texture_selected = math.random(c)
|
||||
end
|
||||
if type(def.textures[1]) == "string" then def.textures = {def.textures} end
|
||||
|
||||
self.texture_selected = self.texture_selected or math.random(#def.textures)
|
||||
self.base_texture = def.textures[self.texture_selected]
|
||||
self.base_mesh = def.mesh
|
||||
self.base_size = self.visual_size
|
||||
|
@ -181,38 +149,19 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.base_selbox = self.selectionbox
|
||||
end
|
||||
|
||||
if not self.base_selbox then
|
||||
self.base_selbox = self.selectionbox or self.base_colbox
|
||||
end
|
||||
self.base_selbox = self.base_selbox or self.selectionbox or self.base_colbox
|
||||
|
||||
local textures = self.base_texture
|
||||
local mesh = self.base_mesh
|
||||
local vis_size = self.base_size
|
||||
local colbox = self.base_colbox
|
||||
local selbox = self.base_selbox
|
||||
self.textures = self.gotten and def.gotten_texture or self.base_texture
|
||||
self.mesh = self.gotten and def.gotten_mesh or self.base_mesh
|
||||
self.visual_size = self.base_size
|
||||
self.collisionbox = self.base_colbox
|
||||
self.selectionbox = self.base_selbox
|
||||
|
||||
if self.gotten == true
|
||||
and def.gotten_texture then
|
||||
textures = def.gotten_texture
|
||||
end
|
||||
if self.child then
|
||||
self.visual_size = { x = self.base_size.x * .5, y = self.base_size.y * .5 }
|
||||
self.textures = def.child_texture and def.child_texture[1] or self.textures
|
||||
|
||||
if self.gotten == true
|
||||
and def.gotten_mesh then
|
||||
mesh = def.gotten_mesh
|
||||
end
|
||||
|
||||
if self.child == true then
|
||||
|
||||
vis_size = {
|
||||
x = self.base_size.x * .5,
|
||||
y = self.base_size.y * .5,
|
||||
}
|
||||
|
||||
if def.child_texture then
|
||||
textures = def.child_texture[1]
|
||||
end
|
||||
|
||||
colbox = {
|
||||
self.collisionbox = {
|
||||
self.base_colbox[1] * .5,
|
||||
self.base_colbox[2] * .5,
|
||||
self.base_colbox[3] * .5,
|
||||
|
@ -220,7 +169,7 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.base_colbox[5] * .5,
|
||||
self.base_colbox[6] * .5
|
||||
}
|
||||
selbox = {
|
||||
self.selectionbox = {
|
||||
self.base_selbox[1] * .5,
|
||||
self.base_selbox[2] * .5,
|
||||
self.base_selbox[3] * .5,
|
||||
|
@ -230,16 +179,12 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
}
|
||||
end
|
||||
|
||||
if self.health == 0 then
|
||||
self.health = math.random (self.hp_min, self.hp_max)
|
||||
end
|
||||
if self.breath == nil then
|
||||
self.breath = self.breath_max
|
||||
end
|
||||
self.health = (self.health and self.health > 0 and self.health) or math.random(self.hp_min, self.hp_max)
|
||||
self.breath = self.breath or self.breath_max
|
||||
|
||||
self.path = {}
|
||||
self.path.way = {} -- path to follow, table of positions
|
||||
self.path.lastpos = {x = 0, y = 0, z = 0}
|
||||
self.path.lastpos = vector.zero()
|
||||
self.path.stuck = false
|
||||
self.path.following = false -- currently following path?
|
||||
self.path.stuck_timer = 0 -- if stuck for too long search for path
|
||||
|
@ -258,13 +203,11 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.old_y = self.object:get_pos().y
|
||||
self.old_health = self.health
|
||||
self.sounds.distance = self.sounds.distance or 10
|
||||
self.textures = textures
|
||||
self.mesh = mesh
|
||||
self.collisionbox = colbox
|
||||
self.selectionbox = selbox
|
||||
self.visual_size = vis_size
|
||||
self.standing_in = "ignore"
|
||||
self.standing_on = "ignore"
|
||||
self.standing_in = mcl_mobs.NODE_IGNORE
|
||||
self.standing_on = mcl_mobs.NODE_IGNORE
|
||||
self.standing_under = mcl_mobs.NODE_IGNORE
|
||||
self.standing_depth = 0
|
||||
self.state = self.state or "stand"
|
||||
self.jump_sound_cooloff = 0 -- used to prevent jump sound from being played too often in short time
|
||||
self.opinion_sound_cooloff = 0 -- used to prevent sound spam of particular sound types
|
||||
|
||||
|
@ -276,42 +219,24 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.blinktimer = 0
|
||||
self.blinkstatus = false
|
||||
|
||||
if not self.nametag then
|
||||
self.nametag = def.nametag
|
||||
end
|
||||
if not self.custom_visual_size then
|
||||
self.visual_size = nil
|
||||
self.base_size = self.visual_size
|
||||
if self.child then
|
||||
self.visual_size = {
|
||||
x = self.visual_size.x * 0.5,
|
||||
y = self.visual_size.y * 0.5,
|
||||
}
|
||||
end
|
||||
end
|
||||
self.acceleration = vector.zero()
|
||||
|
||||
self.nametag = self.nametag or def.nametag
|
||||
|
||||
self.object:set_properties(self)
|
||||
self:set_yaw( (math.random(0, 360) - 180) / 180 * math.pi, 6)
|
||||
self:set_yaw(math.random() * math.pi * 2, 6)
|
||||
self:update_tag()
|
||||
self._current_animation = nil
|
||||
self:set_animation( "stand")
|
||||
self:set_animation("stand")
|
||||
|
||||
|
||||
if self.riden_by_jock then --- Keep this function before self.on_spawn() is run.
|
||||
if self.riden_by_jock then --- Keep this function before self:on_spawn()
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
if self.on_spawn and not self.on_spawn_run and self:on_spawn() then self.on_spawn_run = true end
|
||||
|
||||
if self.on_spawn and not self.on_spawn_run then
|
||||
if self.on_spawn(self) then
|
||||
self.on_spawn_run = true
|
||||
end
|
||||
end
|
||||
|
||||
if not self.wears_armor and self.armor_list then
|
||||
self.armor_list = nil
|
||||
end
|
||||
if not self.wears_armor and self.armor_list then self.armor_list = nil end
|
||||
|
||||
if not self._run_armor_init and self.wears_armor then
|
||||
self.armor_list={helmet="",chestplate="",boots="",leggings=""}
|
||||
|
@ -319,15 +244,10 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self._run_armor_init = true
|
||||
end
|
||||
|
||||
if not self._mcl_potions then
|
||||
self._mcl_potions = {}
|
||||
end
|
||||
if not self._mcl_potions then self._mcl_potions = {} end
|
||||
mcl_potions._load_entity_effects(self)
|
||||
|
||||
|
||||
if def.after_activate then
|
||||
def.after_activate(self, staticdata, def, dtime)
|
||||
end
|
||||
if def.after_activate then def.after_activate(self, staticdata, def, dtime) end
|
||||
end
|
||||
|
||||
-- execute current state (stand, walk, run, attacks)
|
||||
|
@ -346,9 +266,7 @@ function mob_class:do_states(dtime, player_in_active_range)
|
|||
if self.state == PATHFINDING then
|
||||
self:check_gowp(dtime)
|
||||
elseif self.state == "attack" then
|
||||
if self:do_states_attack(dtime) then
|
||||
return true
|
||||
end
|
||||
if self:do_states_attack(dtime) then return true end
|
||||
else
|
||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_dostates", 1) then
|
||||
if self.state == "stand" then
|
||||
|
@ -364,68 +282,46 @@ end
|
|||
|
||||
function mob_class:outside_limits()
|
||||
local pos = self.object:get_pos()
|
||||
if pos then
|
||||
local posx = math.abs(pos.x)
|
||||
local posy = math.abs(pos.y)
|
||||
local posz = math.abs(pos.z)
|
||||
if posx > MAPGEN_MOB_LIMIT or posy > MAPGEN_MOB_LIMIT or posz > MAPGEN_MOB_LIMIT then
|
||||
--minetest.log("action", "Getting close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then
|
||||
minetest.log("action", "Warning mob past limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
else
|
||||
if self.state ~= "stand" then
|
||||
minetest.log("action", "Warning mob close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
self.state = "stand"
|
||||
self:set_animation("stand")
|
||||
self.object:set_acceleration(vector.zero())
|
||||
self.object:set_velocity(vector.zero())
|
||||
end
|
||||
end
|
||||
return true
|
||||
if not pos then return end
|
||||
local posx, posy, posz = math.abs(pos.x), math.abs(pos.y), math.abs(pos.z)
|
||||
if posx > MAPGEN_MOB_LIMIT or posy > MAPGEN_MOB_LIMIT or posz > MAPGEN_MOB_LIMIT then
|
||||
--minetest.log("action", "Getting close to limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then
|
||||
minetest.log("action", "Warning mob past limits of worldgen: " .. minetest.pos_to_string(pos))
|
||||
else
|
||||
self:turn_in_direction(-posx, -posz, 1) -- turn to world spawn
|
||||
self.state = "walk"
|
||||
self:set_animation("walk")
|
||||
self:set_velocity(self.walk_velocity)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function on_step_work(self, dtime, moveresult)
|
||||
local pos = self.object:get_pos()
|
||||
if not pos then return end
|
||||
|
||||
if self:check_despawn(pos, dtime) then return true end
|
||||
if not pos or self._removed then return end
|
||||
if self:outside_limits() then return end
|
||||
if self:check_despawn(pos, dtime) then return end
|
||||
|
||||
-- Start: Death/damage processing
|
||||
-- All damage needs to be undertaken at the start. We need to exit processing if the mob dies.
|
||||
if self:check_death_and_slow_mob() then
|
||||
--minetest.log("action", "Mob is dying: ".. tostring(self.name))
|
||||
-- Do we abandon out of here now?
|
||||
end
|
||||
|
||||
if self:falling(pos, moveresult) then return end
|
||||
if self:step_damage (dtime, pos) then return end
|
||||
pos = self:limit_vel_acc_for_large_dtime(pos, dtime, moveresult) -- limit maximum movement to reduce lag effects
|
||||
self:update_standing(pos, moveresult) -- update what we know of the mobs environment for physics and movement
|
||||
local player_in_active_range = self:player_in_active_range()
|
||||
-- The following functions return true when the mob died and we should stop processing
|
||||
if self:check_suspend(player_in_active_range) then return end
|
||||
-- initializes self.acceleration:
|
||||
if self:gravity_and_floating(pos, dtime, moveresult) then return end -- keep early, for gravity!
|
||||
if self:check_dying() then return end
|
||||
if self:step_damage(dtime, pos) then return end
|
||||
self:check_water_flow(dtime, pos)
|
||||
|
||||
if self.state == "die" then return end
|
||||
-- End: Death/damage processing
|
||||
|
||||
local player_in_active_range = self:player_in_active_range()
|
||||
self:check_suspend(player_in_active_range)
|
||||
|
||||
self:check_water_flow()
|
||||
|
||||
if not self._jumping_cliff then
|
||||
self._can_jump_cliff = self:can_jump_cliff()
|
||||
else
|
||||
self._can_jump_cliff = false
|
||||
end
|
||||
|
||||
self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff()
|
||||
self:flop()
|
||||
|
||||
self:check_smooth_rotation(dtime)
|
||||
self:smooth_rotation(dtime)
|
||||
|
||||
if player_in_active_range then
|
||||
self:set_animation_speed() -- set animation speed relative to velocity
|
||||
|
||||
self:check_head_swivel(dtime)
|
||||
|
||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_engage", 0.2) then
|
||||
|
@ -442,91 +338,76 @@ local function on_step_work(self, dtime, moveresult)
|
|||
end
|
||||
|
||||
if mcl_util.check_dtime_timer(self, dtime, "onstep_occassional", 1) then
|
||||
|
||||
if player_in_active_range then
|
||||
self:check_item_pickup()
|
||||
self:set_armor_texture()
|
||||
self:step_opinion_sound(dtime)
|
||||
end
|
||||
|
||||
self:check_breeding()
|
||||
end
|
||||
|
||||
self:check_aggro(dtime)
|
||||
|
||||
self:check_particlespawners(dtime)
|
||||
|
||||
if self.do_custom and self.do_custom(self, dtime) == false then return end
|
||||
|
||||
if self.do_custom and self:do_custom(dtime) == false then return end
|
||||
if self:do_states(dtime, player_in_active_range) then return end
|
||||
|
||||
self:smooth_acceleration(dtime)
|
||||
local cx, cz = self:collision()
|
||||
self.object:add_velocity(vector.new(cx, 0, cz))
|
||||
self:update_vel_acc(dtime) -- applies self.acceleration
|
||||
if mobs_debug then self:update_tag() end
|
||||
|
||||
if not self.object:get_luaentity() then
|
||||
return false
|
||||
end
|
||||
if not self.object:get_luaentity() then return false end
|
||||
end
|
||||
|
||||
local last_crash_warn_time = 0
|
||||
|
||||
local function log_error (stack_trace, info, info2)
|
||||
local function log_error(stack_trace, info, info2)
|
||||
minetest.log("action", "--- Bug report start (please provide a few lines before this also for context) ---")
|
||||
minetest.log("action", "Error: " .. stack_trace)
|
||||
minetest.log("action", "Bug info: " .. info)
|
||||
if info2 then
|
||||
minetest.log("action", "Bug info additional: " .. info2)
|
||||
end
|
||||
if info2 then minetest.log("action", "Bug info additional: " .. info2) end
|
||||
minetest.log("action", "--- Bug report end ---")
|
||||
end
|
||||
|
||||
local function warn_user_error ()
|
||||
local current_time = os.time()
|
||||
local time_since_warning = current_time - last_crash_warn_time
|
||||
|
||||
--minetest.log("previous_crash_time: " .. current_time)
|
||||
--minetest.log("last_crash_time: " .. last_crash_warn_time)
|
||||
--minetest.log("time_since_warning: " .. time_since_warning)
|
||||
|
||||
if time_since_warning > CRASH_WARN_FREQUENCY then
|
||||
last_crash_warn_time = current_time
|
||||
minetest.log("A game crashing bug was prevented. Please provide debug.log information to VoxeLibre dev team for investigation. (Search for: --- Bug report start)")
|
||||
end
|
||||
end
|
||||
|
||||
local on_step_error_handler = function ()
|
||||
warn_user_error ()
|
||||
local on_step_error_handler = function()
|
||||
warn_user_error()
|
||||
local info = debug.getinfo(1, "SnlufL")
|
||||
log_error(tostring(debug.traceback()), dump(info))
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- main mob function
|
||||
function mob_class:on_step(dtime, moveresult)
|
||||
if not DEVELOPMENT then
|
||||
-- Removed as bundled Lua (5.1 doesn't support xpcall)
|
||||
--local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime)
|
||||
local status, retVal = pcall(on_step_work, self, dtime, moveresult)
|
||||
if status then
|
||||
return retVal
|
||||
else
|
||||
warn_user_error ()
|
||||
local pos = self.object:get_pos()
|
||||
if pos then
|
||||
local node = minetest.get_node(pos)
|
||||
if node and node.name == "ignore" then
|
||||
minetest.log("warning", "Pos is ignored: " .. dump(pos))
|
||||
end
|
||||
end
|
||||
log_error (dump(retVal), dump(pos), dump(self))
|
||||
end
|
||||
-- allow crash in development mode
|
||||
if DEVELOPMENT then return on_step_work(self, dtime, moveresult) end
|
||||
-- Removed as bundled Lua (5.1 doesn't support xpcall)
|
||||
local status, retVal
|
||||
if xpcall then
|
||||
status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime, moveresult)
|
||||
else
|
||||
return on_step_work (self, dtime, moveresult)
|
||||
status, retVal = pcall(on_step_work, self, dtime, moveresult)
|
||||
end
|
||||
if status then return retVal end
|
||||
warn_user_error()
|
||||
local pos = self.object:get_pos()
|
||||
if pos then
|
||||
local node = minetest.get_node(pos)
|
||||
if node and node.name == "ignore" then minetest.log("warning", "Pos is ignored: " .. dump(pos)) end
|
||||
end
|
||||
log_error(dump(retVal), dump(pos), dump(self))
|
||||
end
|
||||
|
||||
local timer = 0
|
||||
|
||||
local function update_lifetimer(dtime)
|
||||
timer = timer + dtime
|
||||
if timer < 1 then return end
|
||||
|
@ -543,10 +424,7 @@ local function update_lifetimer(dtime)
|
|||
timer = 0
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
update_lifetimer(dtime)
|
||||
end)
|
||||
|
||||
minetest.register_globalstep(update_lifetimer)
|
||||
|
||||
minetest.register_chatcommand("clearmobs", {
|
||||
privs = { maphack = true },
|
||||
|
@ -560,11 +438,7 @@ minetest.register_chatcommand("clearmobs", {
|
|||
S("Default usage. Clearing hostile mobs. For more options please type: /help clearmobs"))
|
||||
end
|
||||
local mob, unsafe = param:match("^([%w]+)[ ]?([%w%d]*)$")
|
||||
|
||||
local all = false
|
||||
local nametagged = false
|
||||
local tamed = false
|
||||
|
||||
local all, nametagged, tamed = false, false, false
|
||||
local mob_name, mob_type, range
|
||||
|
||||
-- Param 1 resolve
|
||||
|
@ -578,12 +452,7 @@ minetest.register_chatcommand("clearmobs", {
|
|||
end
|
||||
--minetest.log ("mob: [" .. mob .. "]")
|
||||
else
|
||||
--minetest.log("No valid first param")
|
||||
if default then
|
||||
--minetest.log("Use default")
|
||||
mob_type = "monster"
|
||||
end
|
||||
--return
|
||||
if default then mob_type = "monster" end
|
||||
end
|
||||
|
||||
-- Param 2 resolve
|
||||
|
@ -600,7 +469,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||
end
|
||||
|
||||
local p = minetest.get_player_by_name(player)
|
||||
|
||||
for _,o in pairs(minetest.luaentities) do
|
||||
if o and o.is_mob then
|
||||
local mob_match = false
|
||||
|
@ -609,7 +477,6 @@ minetest.register_chatcommand("clearmobs", {
|
|||
--minetest.log("Match - All mobs specified")
|
||||
mob_match = true
|
||||
elseif mob_type then
|
||||
|
||||
--minetest.log("Match - o.type: ".. tostring(o.type))
|
||||
--minetest.log("mob_type: ".. tostring(mob_type))
|
||||
if mob_type == "monster" and o.type == mob_type then
|
||||
|
@ -618,49 +485,29 @@ minetest.register_chatcommand("clearmobs", {
|
|||
elseif mob_type == "passive" and o.type ~= "monster" and o.type ~= "npc" then
|
||||
--minetest.log("Match - passive")
|
||||
mob_match = true
|
||||
else
|
||||
--minetest.log("No match for type.")
|
||||
--else
|
||||
-- minetest.log("No match for type.")
|
||||
end
|
||||
|
||||
elseif mob_name and (o.name == mob_name or string.find(o.name, mob_name)) then
|
||||
--minetest.log("Match - mob_name = ".. tostring(o.name))
|
||||
mob_match = true
|
||||
else
|
||||
--minetest.log("No match - o.type = ".. tostring(o.type))
|
||||
--minetest.log("No match - mob_name = ".. tostring(o.name))
|
||||
--minetest.log("No match - mob_type = ".. tostring(mob_name))
|
||||
--else
|
||||
-- minetest.log("No match - o.type = ".. tostring(o.type))
|
||||
-- minetest.log("No match - mob_name = ".. tostring(o.name))
|
||||
-- minetest.log("No match - mob_type = ".. tostring(mob_name))
|
||||
end
|
||||
|
||||
if mob_match then
|
||||
local in_range = true
|
||||
if (not range or range <= 0 ) then
|
||||
in_range = true
|
||||
else
|
||||
if ( vector.distance(p:get_pos(),o.object:get_pos()) <= range ) then
|
||||
in_range = true
|
||||
else
|
||||
--minetest.log("Out of range")
|
||||
in_range = false
|
||||
end
|
||||
end
|
||||
|
||||
--minetest.log("o.nametag: ".. tostring(o.nametag))
|
||||
|
||||
local in_range = (not range or range <= 0) or vector.distance(p:get_pos(), o.object:get_pos()) <= range
|
||||
if nametagged then
|
||||
if o.nametag then
|
||||
--minetest.log("Namedtagged and it has a name tag. Kill it")
|
||||
o.object:remove()
|
||||
end
|
||||
if o.nametag then o.object:remove() end
|
||||
elseif tamed then
|
||||
if o.tamed then
|
||||
--minetest.log("Tamed. Kill it")
|
||||
o.object:remove()
|
||||
end
|
||||
if o.tamed then o.object:remove() end
|
||||
elseif in_range and (not o.nametag or o.nametag == "") and not o.tamed then
|
||||
--minetest.log("No nametag or tamed. Kill it")
|
||||
o.object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end})
|
||||
end
|
||||
})
|
||||
|
|
|
@ -58,6 +58,7 @@ functions needed for the mob to work properly which contains the following:
|
|||
that are not liquids
|
||||
'runaway' if true causes animals to turn and run away when hit.
|
||||
'view_range' how many nodes in distance the mob can see a player.
|
||||
'see_through_opaque' override whether the mob can see through opaque nodes
|
||||
'damage' how many health points the mob does to a player or another
|
||||
mob when melee attacking.
|
||||
'knock_back' when true has mobs falling backwards when hit, the greater
|
||||
|
|
|
@ -5,7 +5,7 @@ local HORNY_TIME = 30
|
|||
local HORNY_AGAIN_TIME = 30 -- was 300 or 15*20
|
||||
local CHILD_GROW_TIME = 60
|
||||
|
||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false)
|
||||
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager", false)
|
||||
|
||||
local LOG_MODULE = "[mcl_mobs]"
|
||||
local function mcl_log (message)
|
||||
|
@ -29,18 +29,13 @@ end
|
|||
|
||||
-- feeding, taming and breeding (thanks blert2112)
|
||||
function mob_class:feed_tame(clicker, feed_count, breed, tame, notake)
|
||||
if not self.follow then
|
||||
return false
|
||||
end
|
||||
if clicker:get_wielded_item():get_definition()._mcl_not_consumable then
|
||||
return false
|
||||
end
|
||||
if not self.follow then return false end
|
||||
if clicker:get_wielded_item():get_definition()._mcl_not_consumable then return false end
|
||||
-- can eat/tame with item in hand
|
||||
if self.nofollow or self:follow_holding(clicker) then
|
||||
local consume_food = false
|
||||
|
||||
-- tame if not still a baby
|
||||
|
||||
if tame and not self.child then
|
||||
if not self.owner or self.owner == "" then
|
||||
self.tamed = true
|
||||
|
@ -50,27 +45,20 @@ function mob_class:feed_tame(clicker, feed_count, breed, tame, notake)
|
|||
end
|
||||
|
||||
-- increase health
|
||||
|
||||
if self.health < self.hp_max and not consume_food then
|
||||
consume_food = true
|
||||
self.health = math.min(self.health + 4, self.hp_max)
|
||||
|
||||
if self.htimer < 1 then
|
||||
self.htimer = 5
|
||||
end
|
||||
self.object:set_hp(self.health)
|
||||
end
|
||||
|
||||
-- make children grow quicker
|
||||
|
||||
if not consume_food and self.child == true then
|
||||
if not consume_food and self.child then
|
||||
consume_food = true
|
||||
-- deduct 10% of the time to adulthood
|
||||
self.hornytimer = self.hornytimer + ((CHILD_GROW_TIME - self.hornytimer) * 0.1)
|
||||
end
|
||||
|
||||
-- breed animals
|
||||
|
||||
if breed and not consume_food and self.hornytimer == 0 and not self.horny then
|
||||
self.food = (self.food or 0) + 1
|
||||
consume_food = true
|
||||
|
@ -106,24 +94,15 @@ end
|
|||
-- Spawn a child
|
||||
function mcl_mobs.spawn_child(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()
|
||||
mcl_mobs.effect(pos, 15, "mcl_particles_smoke.png", 1, 2, 2, 15, 5)
|
||||
|
||||
ent.child = true
|
||||
|
||||
local textures
|
||||
-- using specific child texture (if found)
|
||||
if ent.child_texture then
|
||||
textures = ent.child_texture[1]
|
||||
end
|
||||
mcl_mobs.effect(pos, 15, "mcl_particles_smoke.png", 1, 2, 2, 15, 5)
|
||||
|
||||
-- and resize to half height
|
||||
child:set_properties({
|
||||
textures = textures,
|
||||
textures = ent.child_texture and ent.child_texture[1],
|
||||
visual_size = {
|
||||
x = ent.base_size.x * .5,
|
||||
y = ent.base_size.y * .5,
|
||||
|
@ -155,16 +134,12 @@ end
|
|||
|
||||
-- find two animals of same type and breed if nearby and horny
|
||||
function mob_class:check_breeding()
|
||||
|
||||
--mcl_log("In breed function")
|
||||
-- child takes a long time before growing into adult
|
||||
if self.child == true then
|
||||
|
||||
if self.child then
|
||||
-- When a child, hornytimer is used to count age until adulthood
|
||||
self.hornytimer = self.hornytimer + 1
|
||||
|
||||
if self.hornytimer >= CHILD_GROW_TIME then
|
||||
|
||||
self.child = false
|
||||
self.hornytimer = 0
|
||||
|
||||
|
@ -181,11 +156,7 @@ function mob_class:check_breeding()
|
|||
self.on_grown(self)
|
||||
else
|
||||
-- jump when fully grown so as not to fall into ground
|
||||
self.object:set_velocity({
|
||||
x = 0,
|
||||
y = self.jump_height,
|
||||
z = 0
|
||||
})
|
||||
self.object:set_velocity(vector.new(0, self.jump_height, 0))
|
||||
end
|
||||
|
||||
self.animation = nil
|
||||
|
@ -193,121 +164,79 @@ function mob_class:check_breeding()
|
|||
self._current_animation = nil -- Mobs Redo does nothing otherwise
|
||||
self:set_animation(anim)
|
||||
end
|
||||
|
||||
return
|
||||
else
|
||||
-- horny animal can mate for HORNY_TIME seconds,
|
||||
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
|
||||
if self.horny == true then
|
||||
self.hornytimer = self.hornytimer + 1
|
||||
end
|
||||
-- horny animal can mate for HORNY_TIME seconds,
|
||||
-- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds
|
||||
if self.horny == true then
|
||||
self.hornytimer = self.hornytimer + 1
|
||||
|
||||
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
|
||||
self.hornytimer = 0
|
||||
self.horny = false
|
||||
end
|
||||
if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then
|
||||
self.hornytimer = 0
|
||||
self.horny = false
|
||||
end
|
||||
end
|
||||
|
||||
-- find another same animal who is also horny and mate if nearby
|
||||
if self.horny == true
|
||||
and self.hornytimer <= HORNY_TIME then
|
||||
|
||||
if self.horny and self.hornytimer <= HORNY_TIME then
|
||||
mcl_log("In breed function. All good. Do the magic.")
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
mcl_mobs.effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1)
|
||||
mcl_mobs.effect(vector.new(pos.x, pos.y + 1, pos.z), 8, "heart.png", 3, 4, 1, 0.1)
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(pos, 3)
|
||||
local num = 0
|
||||
local ent = nil
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
ent = objs[n]:get_luaentity()
|
||||
local ent = objs[n]:get_luaentity()
|
||||
|
||||
-- check for same animal with different colour
|
||||
local canmate = false
|
||||
|
||||
if ent then
|
||||
|
||||
if ent.name == self.name then
|
||||
canmate = true
|
||||
else
|
||||
local entname = string.split(ent.name,":")
|
||||
local selfname = string.split(self.name,":")
|
||||
|
||||
if entname[1] == selfname[1] then
|
||||
entname = string.split(entname[2],"_")
|
||||
selfname = string.split(selfname[2],"_")
|
||||
|
||||
if entname[1] == selfname[1] then
|
||||
canmate = true
|
||||
end
|
||||
if entname[1] == selfname[1] then canmate = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if canmate then mcl_log("In breed function. Can mate.") end
|
||||
|
||||
if ent
|
||||
and canmate == true
|
||||
and ent.horny == true
|
||||
and ent.hornytimer <= HORNY_TIME then
|
||||
if ent and canmate and ent.horny and ent.hornytimer <= HORNY_TIME then
|
||||
num = num + 1
|
||||
end
|
||||
|
||||
-- found your mate? then have a baby
|
||||
if num > 1 then
|
||||
|
||||
self.hornytimer = HORNY_TIME + 1
|
||||
ent.hornytimer = HORNY_TIME + 1
|
||||
|
||||
-- spawn baby
|
||||
|
||||
|
||||
minetest.after(5, function(parent1, parent2, pos)
|
||||
if not parent1.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not parent2.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not parent1.object:get_luaentity() then return end
|
||||
if not parent2.object:get_luaentity() then return end
|
||||
|
||||
mcl_experience.throw_xp(pos, math.random(1, 7) + (parent1._luck or 0) + (parent2._luck or 0))
|
||||
|
||||
-- custom breed function
|
||||
if parent1.on_breed then
|
||||
-- when false, skip going any further
|
||||
if parent1.on_breed(parent1, parent2) == false then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if parent1.on_breed and not parent1.on_breed(parent1, parent2) then return end
|
||||
pos = vector.round(pos)
|
||||
local child = mcl_mobs.spawn_child(pos, parent1.name)
|
||||
if not child then return end
|
||||
|
||||
local ent_c = child:get_luaentity()
|
||||
|
||||
|
||||
-- Use texture of one of the parents
|
||||
local p = math.random(1, 2)
|
||||
if p == 1 then
|
||||
ent_c.base_texture = parent1.base_texture
|
||||
else
|
||||
ent_c.base_texture = parent2.base_texture
|
||||
end
|
||||
child:set_properties({
|
||||
textures = ent_c.base_texture
|
||||
})
|
||||
ent_c.base_texture = math.random(1, 2) == 1 and parent1.base_texture or parent2.base_texture
|
||||
child:set_properties({ textures = ent_c.base_texture })
|
||||
|
||||
-- tamed and owned by parents' owner
|
||||
ent_c.tamed = true
|
||||
ent_c.owner = parent1.owner
|
||||
end, self, ent, pos)
|
||||
|
||||
num = 0
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
@ -315,9 +244,7 @@ function mob_class:check_breeding()
|
|||
end
|
||||
|
||||
function mob_class:toggle_sit(clicker,p)
|
||||
if not self.tamed or self.child or self.owner ~= clicker:get_player_name() then
|
||||
return
|
||||
end
|
||||
if not self.tamed or self.child or self.owner ~= clicker:get_player_name() then return end
|
||||
local pos = self.object:get_pos()
|
||||
local particle
|
||||
if not self.order or self.order == "" or self.order == "sit" then
|
||||
|
@ -345,7 +272,7 @@ function mob_class:toggle_sit(clicker,p)
|
|||
-- Display icon to show current order (sit or roam)
|
||||
minetest.add_particle({
|
||||
pos = vector.add(pos, pp),
|
||||
velocity = {x=0,y=0.2,z=0},
|
||||
velocity = vector.new(0, 0.2, 0),
|
||||
expirationtime = 1,
|
||||
size = 4,
|
||||
texture = particle,
|
||||
|
|
|
@ -3,6 +3,7 @@ local mob_class = mcl_mobs.mob_class
|
|||
|
||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||
local mobs_see_through_opaque = mcl_mobs.see_through_opaque
|
||||
|
||||
-- pathfinding settings
|
||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||
|
@ -11,39 +12,33 @@ local stuck_path_timeout = 10 -- how long will mob follow path before giving up
|
|||
local enable_pathfinding = true
|
||||
|
||||
local TIME_TO_FORGET_TARGET = 15
|
||||
|
||||
local atann = math.atan
|
||||
local function atan(x)
|
||||
if not x or x ~= x then
|
||||
return 0
|
||||
else
|
||||
return atann(x)
|
||||
end
|
||||
end
|
||||
|
||||
local PI = math.pi
|
||||
local HALFPI = PI * 0.5
|
||||
local random = math.random
|
||||
local min = math.min
|
||||
local floor = math.floor
|
||||
local ceil = math.ceil
|
||||
local abs = math.abs
|
||||
local cos = math.cos
|
||||
local sin = math.sin
|
||||
local atan2 = math.atan2
|
||||
local sqrt = math.sqrt
|
||||
local vector_offset = vector.offset
|
||||
local vector_new = vector.new
|
||||
local vector_copy = vector.copy
|
||||
local vector_distance = vector.distance
|
||||
local vector_zero = vector.zero
|
||||
local node_ok = mcl_mobs.node_ok -- TODO: remove
|
||||
|
||||
-- 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
|
||||
return self.docile_by_day and self.time_of_day > 0.2 and self.time_of_day < 0.8
|
||||
end
|
||||
|
||||
-- get this mob to attack the object
|
||||
function mob_class:do_attack(object)
|
||||
|
||||
if self.state == "attack" or self.state == "die" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if object:is_player() and not minetest.settings:get_bool("enable_damage") then
|
||||
return
|
||||
end
|
||||
if self.state == "attack" or self.state == "die" then return end
|
||||
if object:is_player() and not minetest.settings:get_bool("enable_damage") then return end
|
||||
|
||||
self.attack = object
|
||||
self.state = "attack"
|
||||
|
@ -55,22 +50,19 @@ function mob_class:do_attack(object)
|
|||
end
|
||||
|
||||
-- blast damage to entities nearby
|
||||
local function entity_physics(pos,radius)
|
||||
|
||||
local function entity_physics(pos, radius)
|
||||
radius = radius * 2
|
||||
|
||||
local objs = minetest.get_objects_inside_radius(pos, radius)
|
||||
local obj_pos, dist
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
obj_pos = objs[n]:get_pos()
|
||||
|
||||
dist = vector.distance(pos, obj_pos)
|
||||
dist = vector_distance(pos, obj_pos)
|
||||
if dist < 1 then dist = 1 end
|
||||
|
||||
local damage = math.floor((4 / dist) * radius)
|
||||
local ent = objs[n]:get_luaentity()
|
||||
local damage = floor((4 / dist) * radius)
|
||||
--local ent = objs[n]:get_luaentity()
|
||||
|
||||
-- punches work on entities AND players
|
||||
objs[n]:punch(objs[n], 1.0, {
|
||||
|
@ -87,75 +79,58 @@ local height_switcher = false
|
|||
|
||||
-- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3
|
||||
function mob_class:smart_mobs(s, p, dist, dtime)
|
||||
|
||||
local s1 = self.path.lastpos
|
||||
|
||||
local target_pos = self.attack:get_pos()
|
||||
|
||||
-- is it becoming stuck?
|
||||
if math.abs(s1.x - s.x) + math.abs(s1.z - s.z) < .5 then
|
||||
if abs(s1.x - s.x) + abs(s1.z - s.z) < .5 then
|
||||
self.path.stuck_timer = self.path.stuck_timer + dtime
|
||||
else
|
||||
self.path.stuck_timer = 0
|
||||
end
|
||||
|
||||
self.path.lastpos = {x = s.x, y = s.y, z = s.z}
|
||||
self.path.lastpos = vector_copy(s)
|
||||
|
||||
local use_pathfind = false
|
||||
local has_lineofsight = minetest.line_of_sight(
|
||||
{x = s.x, y = (s.y) + .5, z = s.z},
|
||||
{x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z}, .2)
|
||||
local has_lineofsight = self:line_of_sight(vector_offset(s, 0, .5, 0), vector_offset(target_pos, 0, 1.5, 0),
|
||||
self.see_through_opaque or mobs_see_through_opaque, false)
|
||||
|
||||
-- im stuck, search for path
|
||||
if not has_lineofsight then
|
||||
|
||||
if los_switcher == true then
|
||||
use_pathfind = true
|
||||
los_switcher = false
|
||||
end -- cannot see target!
|
||||
else
|
||||
if los_switcher == false then
|
||||
|
||||
los_switcher = true
|
||||
use_pathfind = false
|
||||
|
||||
minetest.after(1, function(self)
|
||||
if not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not self.object:get_luaentity() then return end
|
||||
if has_lineofsight then self.path.following = false end
|
||||
end, self)
|
||||
end -- can see target!
|
||||
end
|
||||
|
||||
if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
|
||||
|
||||
use_pathfind = true
|
||||
self.path.stuck_timer = 0
|
||||
|
||||
minetest.after(1, function(self)
|
||||
if not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not self.object:get_luaentity() then return end
|
||||
if has_lineofsight then self.path.following = false end
|
||||
end, self)
|
||||
end
|
||||
|
||||
if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
|
||||
|
||||
use_pathfind = true
|
||||
self.path.stuck_timer = 0
|
||||
|
||||
minetest.after(1, function(self)
|
||||
if not self.object:get_luaentity() then
|
||||
return
|
||||
end
|
||||
if not self.object:get_luaentity() then return end
|
||||
if has_lineofsight then self.path.following = false end
|
||||
end, self)
|
||||
end
|
||||
|
||||
if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then
|
||||
|
||||
if abs(s.y - target_pos.y) > self.stepheight then
|
||||
if height_switcher then
|
||||
use_pathfind = true
|
||||
height_switcher = false
|
||||
|
@ -170,35 +145,23 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
if use_pathfind then
|
||||
-- lets try find a path, first take care of positions
|
||||
-- since pathfinder is very sensitive
|
||||
local sheight = self.collisionbox[5] - self.collisionbox[2]
|
||||
--local sheight = self.collisionbox[5] - self.collisionbox[2]
|
||||
|
||||
-- round position to center of node to avoid stuck in walls
|
||||
-- also adjust height for player models!
|
||||
s.x = math.floor(s.x + 0.5)
|
||||
s.z = math.floor(s.z + 0.5)
|
||||
s.x, s.z = floor(s.x + 0.5), floor(s.z + 0.5)
|
||||
|
||||
local ssight, sground = minetest.line_of_sight(s, {
|
||||
x = s.x, y = s.y - 4, z = s.z}, 1)
|
||||
local ssight, sground = minetest.line_of_sight(s, vector_offset(s, 0, -4, 0), 1)
|
||||
|
||||
-- determine node above ground
|
||||
if not ssight then
|
||||
s.y = sground.y + 1
|
||||
end
|
||||
if not ssight then s.y = sground.y + 1 end
|
||||
|
||||
local p1 = self.attack:get_pos()
|
||||
|
||||
p1.x = math.floor(p1.x + 0.5)
|
||||
p1.y = math.floor(p1.y + 0.5)
|
||||
p1.z = math.floor(p1.z + 0.5)
|
||||
p1 = vector_new(floor(p1.x + 0.5), floor(p1.y + 0.5), floor(p1.z + 0.5))
|
||||
|
||||
local dropheight = 12
|
||||
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
||||
local jumpheight = 0
|
||||
if self.jump and self.jump_height >= 4 then
|
||||
jumpheight = math.min(math.ceil(self.jump_height / 4), 4)
|
||||
elseif self.stepheight > 0.5 then
|
||||
jumpheight = 1
|
||||
end
|
||||
local jumpheight = self.jump and floor(self.jump_height + 0.1) or 0
|
||||
self.path.way = minetest.find_path(s, p1, 16, jumpheight, dropheight, "A*_noprefetch")
|
||||
|
||||
self.state = ""
|
||||
|
@ -206,34 +169,26 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
|
||||
-- no path found, try something else
|
||||
if not self.path.way then
|
||||
|
||||
self.path.following = false
|
||||
|
||||
-- lets make way by digging/building if not accessible
|
||||
if self.pathfinding == 2 and mobs_griefing then
|
||||
|
||||
-- is player higher than mob?
|
||||
if s.y < p1.y then
|
||||
|
||||
-- build upwards
|
||||
if not minetest.is_protected(s, "") then
|
||||
|
||||
local ndef1 = minetest.registered_nodes[self.standing_in]
|
||||
|
||||
if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then
|
||||
|
||||
minetest.set_node(s, {name = mcl_mobs.fallback_node})
|
||||
if self.standing_in.buildable_to or self.standing_in.groups.liquid then
|
||||
minetest.set_node(s, {name = mcl_mobs.fallback_node})
|
||||
end
|
||||
end
|
||||
|
||||
local sheight = math.ceil(self.collisionbox[5]) + 1
|
||||
local sheight = ceil(self.collisionbox[5]) + 1
|
||||
|
||||
-- assume mob is 2 blocks high so it digs above its head
|
||||
s.y = s.y + sheight
|
||||
|
||||
-- remove one block above to make room to jump
|
||||
if not minetest.is_protected(s, "") then
|
||||
|
||||
local node1 = node_ok(s, "air").name
|
||||
local ndef1 = minetest.registered_nodes[node1]
|
||||
|
||||
|
@ -243,32 +198,21 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
and not ndef1.groups.level
|
||||
and not ndef1.groups.unbreakable
|
||||
and not ndef1.groups.liquid then
|
||||
|
||||
minetest.set_node(s, {name = "air"})
|
||||
minetest.add_item(s, ItemStack(node1))
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
s.y = s.y - sheight
|
||||
self.object:set_pos({x = s.x, y = s.y + 2, z = s.z})
|
||||
|
||||
self.object:set_pos(vector_offset(s, 0, 2, 0))
|
||||
else -- dig 2 blocks to make door toward player direction
|
||||
|
||||
local yaw1 = self.object:get_yaw() + math.pi / 2
|
||||
local p1 = {
|
||||
x = s.x + math.cos(yaw1),
|
||||
y = s.y,
|
||||
z = s.z + math.sin(yaw1)
|
||||
}
|
||||
local yaw1 = self.object:get_yaw() + HALFPI
|
||||
local p1 = vector_offset(s, cos(yaw1), 0, sin(yaw1))
|
||||
|
||||
if not minetest.is_protected(p1, "") then
|
||||
|
||||
local node1 = node_ok(p1, "air").name
|
||||
local ndef1 = minetest.registered_nodes[node1]
|
||||
|
||||
if node1 ~= "air"
|
||||
and node1 ~= "ignore"
|
||||
if node1 ~= "air" and node1 ~= "ignore"
|
||||
and ndef1
|
||||
and not ndef1.groups.level
|
||||
and not ndef1.groups.unbreakable
|
||||
|
@ -282,8 +226,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
node1 = node_ok(p1, "air").name
|
||||
ndef1 = minetest.registered_nodes[node1]
|
||||
|
||||
if node1 ~= "air"
|
||||
and node1 ~= "ignore"
|
||||
if node1 ~= "air" and node1 ~= "ignore"
|
||||
and ndef1
|
||||
and not ndef1.groups.level
|
||||
and not ndef1.groups.unbreakable
|
||||
|
@ -317,28 +260,19 @@ end
|
|||
|
||||
-- specific attacks
|
||||
local specific_attack = function(list, what)
|
||||
|
||||
-- no list so attack default (player, animals etc.)
|
||||
if list == nil then
|
||||
return true
|
||||
end
|
||||
if list == nil then return true end
|
||||
|
||||
-- found entity on list to attack?
|
||||
for no = 1, #list do
|
||||
|
||||
if list[no] == what then
|
||||
return true
|
||||
end
|
||||
if list[no] == what then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- find someone to attack
|
||||
function mob_class:monster_attack()
|
||||
if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then
|
||||
return
|
||||
end
|
||||
if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then return end
|
||||
|
||||
local s = self.object:get_pos()
|
||||
local p, sp, dist
|
||||
|
@ -392,7 +326,7 @@ function mob_class:monster_attack()
|
|||
p = player:get_pos()
|
||||
sp = s
|
||||
|
||||
dist = vector.distance(p, s)
|
||||
dist = vector_distance(p, s)
|
||||
|
||||
-- aim higher to make looking up hills more realistic
|
||||
p.y = p.y + 1
|
||||
|
@ -414,7 +348,7 @@ function mob_class:monster_attack()
|
|||
end
|
||||
end
|
||||
if not min_player and #blacklist_attack > 0 then
|
||||
min_player=blacklist_attack[math.random(#blacklist_attack)]
|
||||
min_player=blacklist_attack[random(#blacklist_attack)]
|
||||
end
|
||||
-- attack player
|
||||
if min_player then
|
||||
|
@ -425,7 +359,6 @@ end
|
|||
|
||||
-- npc, find closest monster to attack
|
||||
function mob_class:npc_attack()
|
||||
|
||||
if self.type ~= "npc"
|
||||
or not self.attacks_monsters
|
||||
or self.state == "attack" then
|
||||
|
@ -444,13 +377,13 @@ function mob_class:npc_attack()
|
|||
p = obj.object:get_pos()
|
||||
sp = s
|
||||
|
||||
local dist = vector.distance(p, s)
|
||||
local dist = vector_distance(p, s)
|
||||
|
||||
-- aim higher to make looking up hills more realistic
|
||||
p.y = p.y + 1
|
||||
sp.y = sp.y + 1
|
||||
|
||||
if dist < min_dist and self:line_of_sight( sp, p, 2) == true then
|
||||
if dist < min_dist and self:line_of_sight(sp, p, 2) == true then
|
||||
min_dist = dist
|
||||
min_player = obj.object
|
||||
end
|
||||
|
@ -466,19 +399,13 @@ end
|
|||
|
||||
-- dogshoot attack switch and counter function
|
||||
function mob_class:dogswitch(dtime)
|
||||
|
||||
-- switch mode not activated
|
||||
if not self.dogshoot_switch
|
||||
or not dtime then
|
||||
return 0
|
||||
end
|
||||
if not self.dogshoot_switch or not dtime then return 0 end
|
||||
|
||||
self.dogshoot_count = self.dogshoot_count + dtime
|
||||
|
||||
if (self.dogshoot_switch == 1
|
||||
and self.dogshoot_count > self.dogshoot_count_max)
|
||||
or (self.dogshoot_switch == 2
|
||||
and self.dogshoot_count > self.dogshoot_count2_max) then
|
||||
if (self.dogshoot_switch == 1 and self.dogshoot_count > self.dogshoot_count_max)
|
||||
or (self.dogshoot_switch == 2 and self.dogshoot_count > self.dogshoot_count2_max) then
|
||||
|
||||
self.dogshoot_count = 0
|
||||
|
||||
|
@ -525,13 +452,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
if is_player then
|
||||
-- is mob out of reach?
|
||||
if vector.distance(mob_pos, player_pos) > 3 then
|
||||
return
|
||||
end
|
||||
if vector_distance(mob_pos, player_pos) > 3 then return end
|
||||
-- is mob protected?
|
||||
if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then
|
||||
return
|
||||
end
|
||||
if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then return end
|
||||
|
||||
mcl_potions.update_haste_and_fatigue(hitter)
|
||||
end
|
||||
|
@ -540,13 +463,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local time_diff = time_now - self.invul_timestamp
|
||||
|
||||
-- check for invulnerability time in microseconds (0.5 second)
|
||||
if time_diff <= 500000 and time_diff >= 0 then
|
||||
return
|
||||
end
|
||||
if time_diff <= 500000 and time_diff >= 0 then return end
|
||||
|
||||
-- custom punch function
|
||||
if self.do_punch then
|
||||
|
||||
-- when false skip going any further
|
||||
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
||||
return
|
||||
|
@ -562,15 +482,11 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local time_now = minetest.get_us_time()
|
||||
|
||||
if is_player then
|
||||
if minetest.is_creative_enabled(hitter:get_player_name()) then
|
||||
self.health = 0
|
||||
end
|
||||
|
||||
if minetest.is_creative_enabled(hitter:get_player_name()) then self.health = 0 end
|
||||
-- set/update 'drop xp' timestamp if hitted by player
|
||||
self.xp_timestamp = time_now
|
||||
end
|
||||
|
||||
|
||||
-- punch interval
|
||||
local weapon = hitter:get_wielded_item()
|
||||
local punch_interval = 1.4
|
||||
|
@ -591,18 +507,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
end
|
||||
|
||||
|
||||
for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do
|
||||
|
||||
for group,_ in pairs((tool_capabilities.damage_groups or {}) ) do
|
||||
tmp = tflp / (tool_capabilities.full_punch_interval or 1.4)
|
||||
|
||||
if tmp < 0 then
|
||||
tmp = 0.0
|
||||
elseif tmp > 1 then
|
||||
tmp = 1.0
|
||||
end
|
||||
|
||||
damage = damage + (tool_capabilities.damage_groups[group] or 0)
|
||||
* tmp * ((armor[group] or 0) / 100.0)
|
||||
tmp = tmp < 0 and 0 or (tmp > 1 and 1 or tmp)
|
||||
damage = damage + (tool_capabilities.damage_groups[group] or 0) * tmp * ((armor[group] or 0) / 100.0)
|
||||
end
|
||||
|
||||
-- strength and weakness effects
|
||||
|
@ -621,9 +529,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
-- check for tool immunity or special damage
|
||||
for n = 1, #self.immune_to do
|
||||
|
||||
if self.immune_to[n][1] == weapon:get_name() then
|
||||
|
||||
damage = self.immune_to[n][2] or 0
|
||||
break
|
||||
end
|
||||
|
@ -631,7 +537,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
-- healing
|
||||
if damage <= -1 then
|
||||
self.health = self.health - math.floor(damage)
|
||||
self.health = self.health - floor(damage)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -651,7 +557,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local weapon = hitter:get_wielded_item(player)
|
||||
local def = weapon:get_definition()
|
||||
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
||||
local wear = math.floor(65535/tool_capabilities.punch_attack_uses)
|
||||
local wear = floor(65535/tool_capabilities.punch_attack_uses)
|
||||
weapon:add_wear(wear)
|
||||
tt.reload_itemstack_description(weapon) -- update tooltip
|
||||
hitter:set_wielded_item(weapon)
|
||||
|
@ -662,14 +568,12 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
local die = false
|
||||
|
||||
|
||||
if damage >= 0 then
|
||||
-- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately.
|
||||
if damage >= 0.1 then
|
||||
-- weapon sounds
|
||||
if weapon:get_definition().sounds ~= nil then
|
||||
|
||||
local s = math.random(0, #weapon:get_definition().sounds)
|
||||
local s = random(0, #weapon:get_definition().sounds)
|
||||
|
||||
minetest.sound_play(weapon:get_definition().sounds[s], {
|
||||
object = self.object, --hitter,
|
||||
|
@ -696,27 +600,20 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
end
|
||||
end
|
||||
-- knock back effect (only on full punch)
|
||||
if self.knock_back
|
||||
and tflp >= punch_interval then
|
||||
if self.knock_back and tflp >= punch_interval then
|
||||
-- direction error check
|
||||
dir = dir or {x = 0, y = 0, z = 0}
|
||||
dir = dir or vector_zero()
|
||||
|
||||
local v = self.object:get_velocity()
|
||||
if not v then return end
|
||||
local r = 1.4 - math.min(punch_interval, 1.4)
|
||||
local kb = r * (math.abs(v.x)+math.abs(v.z))
|
||||
local r = 1.4 - min(punch_interval, 1.4)
|
||||
local kb = r * sqrt(v.x*v.x+v.z*v.z)
|
||||
local up = 2.625
|
||||
|
||||
if die==true then
|
||||
kb=kb*1.25
|
||||
end
|
||||
if die then kb = kb * 1.25 end
|
||||
|
||||
-- if already in air then dont go up anymore when hit
|
||||
if math.abs(v.y) > 0.1
|
||||
or self.fly then
|
||||
up = 0
|
||||
end
|
||||
|
||||
if abs(v.y) > 0.1 or self.fly then up = 0 end
|
||||
|
||||
-- check if tool already has specific knockback value
|
||||
if tool_capabilities.damage_groups["knockback"] then
|
||||
|
@ -725,34 +622,30 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
kb = kb * 1.25
|
||||
end
|
||||
|
||||
|
||||
local luaentity
|
||||
if hitter then
|
||||
luaentity = hitter:get_luaentity()
|
||||
end
|
||||
local luaentity = hitter and hitter:get_luaentity()
|
||||
if hitter and is_player then
|
||||
local wielditem = hitter:get_wielded_item()
|
||||
kb = kb + 9 * mcl_enchanting.get_enchantment(wielditem, "knockback")
|
||||
-- add player velocity to mob knockback
|
||||
local hv = hitter:get_velocity()
|
||||
local dir_dot = (hv.x * dir.x) + (hv.z * dir.z)
|
||||
local player_mag = math.sqrt((hv.x * hv.x) + (hv.z * hv.z))
|
||||
local mob_mag = math.sqrt((v.x * v.x) + (v.z * v.z))
|
||||
local dir_dot = hv.x * dir.x + hv.z * dir.z
|
||||
local player_mag = sqrt(hv.x * hv.x + hv.z * hv.z)
|
||||
local mob_mag = sqrt(v.x * v.x + v.z * v.z)
|
||||
if dir_dot > 0 and mob_mag <= player_mag * 0.625 then
|
||||
kb = kb + ((math.abs(hv.x) + math.abs(hv.z)) * r)
|
||||
kb = kb + (abs(hv.x) + abs(hv.z)) * r
|
||||
end
|
||||
elseif luaentity and luaentity._knockback and die == false then
|
||||
kb = kb + luaentity._knockback
|
||||
kb = kb + luaentity._knockback * 0.25
|
||||
elseif luaentity and luaentity._knockback and die == true then
|
||||
kb = kb + luaentity._knockback * 0.25
|
||||
end
|
||||
self._kb_turn = true
|
||||
self._turn_to=self.object:get_yaw()-1.57
|
||||
self:turn_by(HALFPI, .1) -- knockback turn
|
||||
self.frame_speed_multiplier=2.3
|
||||
if self.animation.run_end then
|
||||
self:set_animation( "run")
|
||||
self:set_animation("run")
|
||||
elseif self.animation.walk_end then
|
||||
self:set_animation( "walk")
|
||||
self:set_animation("walk")
|
||||
end
|
||||
minetest.after(0.2, function()
|
||||
if self and self.object then
|
||||
|
@ -760,11 +653,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
self._kb_turn = false
|
||||
end
|
||||
end)
|
||||
self.object:add_velocity({
|
||||
x = dir.x * kb,
|
||||
y = up*2,
|
||||
z = dir.z * kb
|
||||
})
|
||||
self.object:add_velocity(vector_new(dir.x * kb, up, dir.z * kb ))
|
||||
|
||||
self.pause_timer = 0.25
|
||||
end
|
||||
|
@ -772,12 +661,15 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
|
||||
-- if skittish then run away
|
||||
if hitter and is_player and hitter:get_pos() and not die and self.runaway == true and self.state ~= "flop" then
|
||||
|
||||
local yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||
local hp, sp = hitter:get_pos(), self.object:get_pos()
|
||||
self:turn_in_direction(sp.x - hp.x, sp.z - hp.z, 1)
|
||||
minetest.after(0.2,function()
|
||||
if self and self.object and self.object:get_pos() and hitter and is_player and hitter:get_pos() then
|
||||
yaw = self:set_yaw( minetest.dir_to_yaw(vector.direction(hitter:get_pos(), self.object:get_pos())))
|
||||
self:set_velocity( self.run_velocity)
|
||||
if self and self.object and hitter and is_player then
|
||||
local hp, sp = hitter:get_pos(), self.object:get_pos()
|
||||
if hp and sp then
|
||||
self:turn_in_direction(sp.x - hp.x, sp.z - hp.z, 1)
|
||||
self:set_velocity(self.run_velocity)
|
||||
end
|
||||
end
|
||||
end)
|
||||
self.state = "runaway"
|
||||
|
@ -805,12 +697,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir)
|
|||
local alert_pos = hitter:get_pos()
|
||||
if alert_pos then
|
||||
local objs = minetest.get_objects_inside_radius(alert_pos, self.view_range)
|
||||
local obj = nil
|
||||
|
||||
for n = 1, #objs do
|
||||
|
||||
obj = objs[n]:get_luaentity()
|
||||
|
||||
local obj = objs[n]:get_luaentity()
|
||||
if obj then
|
||||
-- only alert members of same mob or friends
|
||||
if obj.group_attack
|
||||
|
@ -840,11 +728,7 @@ end
|
|||
|
||||
function mob_class:check_aggro(dtime)
|
||||
if not self._aggro or not self.attack then return end
|
||||
|
||||
if not self._check_aggro_timer then
|
||||
self._check_aggro_timer = 0
|
||||
end
|
||||
|
||||
if not self._check_aggro_timer then self._check_aggro_timer = 0 end
|
||||
if self._check_aggro_timer > 5 then
|
||||
self._check_aggro_timer = 0
|
||||
|
||||
|
@ -852,7 +736,7 @@ function mob_class:check_aggro(dtime)
|
|||
-- TODO consider removing this in favour of what is done in do_states_attack
|
||||
-- Attack is dropped in do_states_attack if out of range, so won't even trigger here
|
||||
-- I do not think this code does anything. Are mobs still loaded in at 128?
|
||||
if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
||||
if not self.attack:get_pos() or vector_distance(self.attack:get_pos(),self.object:get_pos()) > 128 then
|
||||
self._aggro = nil
|
||||
self.attack = nil
|
||||
self.state = "stand"
|
||||
|
@ -878,25 +762,21 @@ local function clear_aggro(self)
|
|||
self.path.way = nil
|
||||
end
|
||||
|
||||
function mob_class:do_states_attack (dtime)
|
||||
function mob_class:do_states_attack(dtime)
|
||||
self.timer = self.timer + dtime
|
||||
if self.timer > 100 then
|
||||
self.timer = 1
|
||||
end
|
||||
if self.timer > 100 then self.timer = 1 end
|
||||
|
||||
local s = self.object:get_pos()
|
||||
if not s then return end
|
||||
local p = self.attack:get_pos()
|
||||
if not s or not p then return end
|
||||
|
||||
local p = self.attack:get_pos() or s
|
||||
|
||||
local yaw = self.object:get_yaw() or 0
|
||||
|
||||
-- stop attacking if player invisible or out of range
|
||||
if not self.attack
|
||||
or not self.attack:get_pos()
|
||||
or not self:object_in_range(self.attack)
|
||||
or self.attack:get_hp() <= 0
|
||||
or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then
|
||||
or (self.attack:is_player() and mcl_mobs.invis[self.attack:get_player_name()]) then
|
||||
|
||||
clear_aggro(self)
|
||||
return
|
||||
|
@ -920,20 +800,15 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
|
||||
-- calculate distance from mob and enemy
|
||||
local dist = vector.distance(p, s)
|
||||
local dist = vector_distance(p, s)
|
||||
|
||||
if self.attack_type == "explode" then
|
||||
|
||||
if target_line_of_sight then
|
||||
local vec = { x = p.x - s.x, z = p.z - s.z }
|
||||
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
||||
if p.x > s.x then yaw = yaw +math.pi end
|
||||
yaw = self:set_yaw( yaw, 0, dtime)
|
||||
self:turn_in_direction(p.x - s.x, p.z - s.z, 1)
|
||||
end
|
||||
|
||||
local node_break_radius = self.explosion_radius or 1
|
||||
local entity_damage_radius = self.explosion_damage_radius
|
||||
or (node_break_radius * 2)
|
||||
local entity_damage_radius = self.explosion_damage_radius or (node_break_radius * 2)
|
||||
|
||||
-- start timer when in reach and line of sight
|
||||
if not self.v_start and dist <= self.reach and target_line_of_sight then
|
||||
|
@ -960,9 +835,9 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
|
||||
if self.animation and self.animation.run_start then
|
||||
self:set_animation( "run")
|
||||
self:set_animation("run")
|
||||
else
|
||||
self:set_animation( "walk")
|
||||
self:set_animation("walk")
|
||||
end
|
||||
|
||||
if self.v_start then
|
||||
|
@ -1005,92 +880,50 @@ function mob_class:do_states_attack (dtime)
|
|||
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy)
|
||||
or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then
|
||||
|
||||
if self.fly
|
||||
and dist > self.reach then
|
||||
|
||||
local p1 = s
|
||||
local me_y = math.floor(p1.y)
|
||||
local p2 = p
|
||||
local p_y = math.floor(p2.y + 1)
|
||||
if self.fly and dist > self.reach then
|
||||
local p1, p2 = s, p
|
||||
local me_y, p_y = floor(p1.y), floor(p2.y + 1)
|
||||
local v = self.object:get_velocity()
|
||||
|
||||
if self:flight_check( s) then
|
||||
|
||||
if me_y < p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = 1 * self.walk_velocity,
|
||||
z = v.z
|
||||
})
|
||||
|
||||
self.object:set_velocity(vector_new(v.x, 1 * self.walk_velocity, v.z))
|
||||
elseif me_y > p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = -1 * self.walk_velocity,
|
||||
z = v.z
|
||||
})
|
||||
self.object:set_velocity(vector_new(v.x, -1 * self.walk_velocity, v.z))
|
||||
end
|
||||
else
|
||||
if me_y < p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = 0.01,
|
||||
z = v.z
|
||||
})
|
||||
|
||||
self.object:set_velocity(vector_new(v.x, 0.01, v.z))
|
||||
elseif me_y > p_y then
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x,
|
||||
y = -0.01,
|
||||
z = v.z
|
||||
})
|
||||
self.object:set_velocity(vector_new(v.x, -0.01, v.z))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- rnd: new movement direction
|
||||
if self.path.following
|
||||
and self.path.way
|
||||
and self.attack_type ~= "dogshoot" then
|
||||
|
||||
if self.path.following and self.path.way and self.attack_type ~= "dogshoot" then
|
||||
-- no paths longer than 50
|
||||
if #self.path.way > 50
|
||||
or dist < self.reach then
|
||||
if #self.path.way > 50 or dist < self.reach then
|
||||
self.path.following = false
|
||||
return
|
||||
end
|
||||
|
||||
local p1 = self.path.way[1]
|
||||
|
||||
if not p1 then
|
||||
self.path.following = false
|
||||
return
|
||||
end
|
||||
|
||||
if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then
|
||||
if abs(p1.x - s.x) + abs(p1.z - s.z) < 0.6 then
|
||||
-- reached waypoint, remove it from queue
|
||||
table.remove(self.path.way, 1)
|
||||
end
|
||||
|
||||
-- set new temporary target
|
||||
p = {x = p1.x, y = p1.y, z = p1.z}
|
||||
p = vector_copy(p1)
|
||||
end
|
||||
|
||||
local vec = {
|
||||
x = p.x - s.x,
|
||||
z = p.z - s.z
|
||||
}
|
||||
|
||||
yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate
|
||||
|
||||
if p.x > s.x then yaw = yaw + math.pi end
|
||||
|
||||
yaw = self:set_yaw( yaw, 0, dtime)
|
||||
self:turn_in_direction(p.x - s.x, p.z - s.z, 10)
|
||||
|
||||
-- move towards enemy if beyond mob reach
|
||||
if dist > self.reach then
|
||||
|
@ -1100,10 +933,9 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
|
||||
if self:is_at_cliff_or_danger() then
|
||||
self:set_velocity( 0)
|
||||
self:set_animation( "stand")
|
||||
local yaw = self.object:get_yaw() or 0
|
||||
yaw = self:set_yaw( yaw + 0.78, 8)
|
||||
self:set_velocity(0)
|
||||
self:set_animation("stand")
|
||||
--self:turn_by(PI * (random() - 0.5), 10)
|
||||
else
|
||||
if self.path.stuck then
|
||||
self:set_velocity(self.walk_velocity)
|
||||
|
@ -1121,7 +953,7 @@ function mob_class:do_states_attack (dtime)
|
|||
self.path.stuck_timer = 0
|
||||
self.path.following = false -- not stuck anymore
|
||||
|
||||
self:set_velocity( 0)
|
||||
self:set_velocity(0)
|
||||
|
||||
local attack_frequency = self.attack_frequency or 1
|
||||
|
||||
|
@ -1129,19 +961,13 @@ function mob_class:do_states_attack (dtime)
|
|||
self.timer = 0
|
||||
|
||||
if not self.custom_attack then
|
||||
if self.double_melee_attack and math.random(1, 2) == 1 then
|
||||
if self.double_melee_attack and random(1, 2) == 1 then
|
||||
self:set_animation("punch2")
|
||||
else
|
||||
self:set_animation("punch")
|
||||
end
|
||||
|
||||
local p2 = p
|
||||
local s2 = s
|
||||
|
||||
p2.y = p2.y + .5
|
||||
s2.y = s2.y + .5
|
||||
|
||||
if self:line_of_sight( p2, s2) == true then
|
||||
if self:line_of_sight(vector_offset(p, 0, .5, 0), vector_offset(s, 0, .5, 0)) == true then
|
||||
self:mob_sound("attack")
|
||||
|
||||
-- punch player (or what player is attached to)
|
||||
|
@ -1167,68 +993,43 @@ function mob_class:do_states_attack (dtime)
|
|||
elseif self.attack_type == "shoot"
|
||||
or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1)
|
||||
or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then
|
||||
|
||||
p.y = p.y - .5
|
||||
s.y = s.y + .5
|
||||
|
||||
local dist = vector.distance(p, s)
|
||||
local vec = {
|
||||
x = p.x - s.x,
|
||||
y = p.y - s.y,
|
||||
z = p.z - s.z
|
||||
}
|
||||
|
||||
yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
||||
|
||||
if p.x > s.x then yaw = yaw +math.pi end
|
||||
|
||||
yaw = self:set_yaw( yaw, 0, dtime)
|
||||
|
||||
local stay_away_from_player = vector.zero()
|
||||
|
||||
--strafe back and fourth
|
||||
|
||||
--stay away from player so as to shoot them
|
||||
if dist < self.avoid_distance and self.shooter_avoid_enemy then
|
||||
self:set_animation( "shoot")
|
||||
stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33)
|
||||
end
|
||||
local vec = vector_new(p.x - s.x, p.y - s.y - 1, p.z - s.z)
|
||||
local dist = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)
|
||||
self:turn_in_direction(vec.x, vec.z, 10)
|
||||
|
||||
if self.strafes then
|
||||
if not self.strafe_direction then
|
||||
self.strafe_direction = 1.57
|
||||
end
|
||||
if math.random(40) == 1 then
|
||||
self.strafe_direction = self.strafe_direction*-1
|
||||
end
|
||||
if not self.strafe_direction then self.strafe_direction = math.random(0, 1) * 2 - 1 end
|
||||
if random(50) == 1 then self.strafe_direction = -self.strafe_direction end
|
||||
|
||||
local dir = vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction)
|
||||
local dir2 = vector.multiply(dir, 0.3 * self.walk_velocity)
|
||||
|
||||
if dir2 and stay_away_from_player then
|
||||
self.acc = vector.add(dir2, stay_away_from_player)
|
||||
--stay away from player so as to shoot them
|
||||
if self.avoid_distance and self.shooter_avoid_enemy then
|
||||
local f = (dist - self.avoid_distance) / self.avoid_distance
|
||||
f = math.max(-1, math.min(1, f))
|
||||
f = f * math.abs(f)
|
||||
self:set_velocity(f * self.walk_velocity, (1 - math.abs(f)) * self.strafe_direction * self.walk_velocity * 1.5)
|
||||
elseif dist > 1 then
|
||||
self:set_velocity(self.walk_velocity)
|
||||
else
|
||||
self:set_velocity(0)
|
||||
end
|
||||
else
|
||||
self:set_velocity( 0)
|
||||
self:set_velocity(0)
|
||||
self:set_animation("stand")
|
||||
end
|
||||
|
||||
local p = self.object:get_pos()
|
||||
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2
|
||||
|
||||
if self.shoot_interval
|
||||
and self.timer > self.shoot_interval
|
||||
and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next()
|
||||
and math.random(1, 100) <= 60 then
|
||||
p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) * 0.5
|
||||
|
||||
if self.shoot_interval and self.timer > self.shoot_interval and random(1, 100) <= 60
|
||||
and not minetest.raycast(vector_offset(p, 0, self.shoot_offset, 0), vector_offset(self.attack:get_pos(), 0, 1.5, 0), false, false):next() then
|
||||
self.timer = 0
|
||||
self:set_animation( "shoot")
|
||||
self:set_animation("shoot")
|
||||
|
||||
-- play shoot attack sound
|
||||
self:mob_sound("shoot_attack")
|
||||
|
||||
-- Shoot arrow
|
||||
if minetest.registered_entities[self.arrow] then
|
||||
|
||||
local arrow, ent
|
||||
local v = 1
|
||||
if not self.shoot_arrow then
|
||||
|
@ -1238,9 +1039,7 @@ function mob_class:do_states_attack (dtime)
|
|||
end)
|
||||
arrow = minetest.add_entity(p, self.arrow)
|
||||
ent = arrow:get_luaentity()
|
||||
if ent.velocity then
|
||||
v = ent.velocity
|
||||
end
|
||||
v = ent.velocity or v
|
||||
ent.switch = 1
|
||||
ent.owner_id = tostring(self.object) -- add unique owner id to arrow
|
||||
|
||||
|
@ -1252,12 +1051,9 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
end
|
||||
|
||||
local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5
|
||||
-- offset makes shoot aim accurate
|
||||
vec.y = vec.y + self.shoot_offset
|
||||
vec.x = vec.x * (v / amount)
|
||||
vec.y = vec.y * (v / amount)
|
||||
vec.z = vec.z * (v / amount)
|
||||
vec.x, vec.y, vec.z = vec.x * (v / dist), vec.y * (v / dist), vec.z * (v / dist)
|
||||
if self.shoot_arrow then
|
||||
vec = vector.normalize(vec)
|
||||
self:shoot_arrow(p, vec)
|
||||
|
@ -1266,13 +1062,9 @@ function mob_class:do_states_attack (dtime)
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
elseif self.attack_type == "custom" and self.attack_state then
|
||||
self.attack_state(self, dtime)
|
||||
end
|
||||
|
||||
if self.on_attack then
|
||||
self.on_attack(self, dtime)
|
||||
end
|
||||
|
||||
if self.on_attack then self.on_attack(self, dtime) end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
local math, tonumber, vector, minetest, mcl_mobs = math, tonumber, vector, minetest, mcl_mobs
|
||||
local mob_class = mcl_mobs.mob_class
|
||||
local validate_vector = mcl_util.validate_vector
|
||||
--local validate_vector = mcl_util.validate_vector
|
||||
|
||||
local active_particlespawners = {}
|
||||
local disable_blood = minetest.settings:get_bool("mobs_disable_blood")
|
||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||
local PI = math.pi
|
||||
local TWOPI = math.pi * 2
|
||||
local PI_HALF = math.pi * 0.5 -- 90 degrees
|
||||
local MAX_PITCH = math.pi * 0.45 -- about 80 degrees
|
||||
local MAX_YAW = math.pi * 0.66 -- about 120 degrees
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
|
||||
|
@ -13,51 +18,31 @@ if player_transfer_distance == 0 then player_transfer_distance = math.huge end
|
|||
|
||||
-- custom particle effects
|
||||
function mcl_mobs.effect(pos, amount, texture, min_size, max_size, radius, gravity, glow, go_down)
|
||||
|
||||
radius = radius or 2
|
||||
min_size = min_size or 0.5
|
||||
max_size = max_size or 1
|
||||
gravity = gravity or DEFAULT_FALL_SPEED
|
||||
glow = glow or 0
|
||||
go_down = go_down or false
|
||||
|
||||
local ym
|
||||
if go_down then
|
||||
ym = 0
|
||||
else
|
||||
ym = -radius
|
||||
end
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = amount,
|
||||
time = 0.25,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minvel = {x = -radius, y = ym, z = -radius},
|
||||
maxvel = {x = radius, y = radius, z = radius},
|
||||
minacc = {x = 0, y = gravity, z = 0},
|
||||
maxacc = {x = 0, y = gravity, z = 0},
|
||||
minvel = vector.new(-radius, go_down and 0 or -radius, -radius),
|
||||
maxvel = vector.new(radius, radius, radius),
|
||||
minacc = vector.new(0, gravity or DEFAULT_FALL_SPEED, 0),
|
||||
maxacc = vector.new(0, gravity or DEFAULT_FALL_SPEED, 0),
|
||||
minexptime = 0.1,
|
||||
maxexptime = 1,
|
||||
minsize = min_size,
|
||||
maxsize = max_size,
|
||||
minsize = min_size or 0.5,
|
||||
maxsize = max_size or 1,
|
||||
texture = texture,
|
||||
glow = glow,
|
||||
glow = glow or 0,
|
||||
})
|
||||
end
|
||||
|
||||
function mcl_mobs.death_effect(pos, yaw, collisionbox, rotate)
|
||||
local min, max
|
||||
if collisionbox then
|
||||
min = {x=collisionbox[1], y=collisionbox[2], z=collisionbox[3]}
|
||||
max = {x=collisionbox[4], y=collisionbox[5], z=collisionbox[6]}
|
||||
else
|
||||
min = { x = -0.5, y = 0, z = -0.5 }
|
||||
max = { x = 0.5, y = 0.5, z = 0.5 }
|
||||
end
|
||||
local min = collisionbox and vector.new(collisionbox[1], collisionbox[2], collisionbox[3]) or vector.new(-0.5, 0, -0.5)
|
||||
local max = collisionbox and vector.new(collisionbox[4], collisionbox[5], collisionbox[6]) or vector.new(0.5, 0.5, 0.5)
|
||||
if rotate then
|
||||
min = vector.rotate(min, {x=0, y=yaw, z=math.pi/2})
|
||||
max = vector.rotate(max, {x=0, y=yaw, z=math.pi/2})
|
||||
min = vector.rotate(min, vector.new(0, yaw, math.pi/2))
|
||||
max = vector.rotate(max, vector.new(0, yaw, math.pi/2))
|
||||
min, max = vector.sort(min, max)
|
||||
min = vector.multiply(min, 0.5)
|
||||
max = vector.multiply(max, 0.5)
|
||||
|
@ -89,57 +74,35 @@ end
|
|||
|
||||
-- play sound
|
||||
function mob_class:mob_sound(soundname, is_opinion, fixed_pitch)
|
||||
|
||||
local soundinfo
|
||||
if self.sounds_child and self.child then
|
||||
soundinfo = self.sounds_child
|
||||
elseif self.sounds then
|
||||
soundinfo = self.sounds
|
||||
end
|
||||
if not soundinfo then
|
||||
return
|
||||
end
|
||||
local soundinfo = self.child and self.sounds_child or self.sounds
|
||||
if not soundinfo then return end
|
||||
local sound = soundinfo[soundname]
|
||||
if sound then
|
||||
if is_opinion and self.opinion_sound_cooloff > 0 then
|
||||
return
|
||||
end
|
||||
local pitch
|
||||
if not fixed_pitch then
|
||||
local base_pitch = soundinfo.base_pitch
|
||||
if not base_pitch then
|
||||
base_pitch = 1
|
||||
end
|
||||
if self.child and (not self.sounds_child) then
|
||||
-- Children have higher pitch
|
||||
pitch = base_pitch * 1.5
|
||||
else
|
||||
pitch = base_pitch
|
||||
end
|
||||
-- randomize the pitch a bit
|
||||
pitch = pitch + math.random(-10, 10) * 0.005
|
||||
end
|
||||
-- Should be 0.1 to 0.2 for mobs. Cow and zombie farms loud. At least have cool down.
|
||||
minetest.sound_play(sound, {
|
||||
object = self.object,
|
||||
gain = 1.0,
|
||||
max_hear_distance = self.sounds.distance,
|
||||
pitch = pitch,
|
||||
}, true)
|
||||
self.opinion_sound_cooloff = 1
|
||||
if not sound then return end
|
||||
if is_opinion and self.opinion_sound_cooloff > 0 then return end
|
||||
local pitch
|
||||
if not fixed_pitch then
|
||||
pitch = soundinfo.base_pitch or 1
|
||||
if self.child and not self.sounds_child then pitch = pitch * 1.5 end
|
||||
pitch = pitch + (math.random() - 0.5) * 0.2
|
||||
end
|
||||
-- Should be 0.1 to 0.2 for mobs. Cow and zombie farms loud. At least have cool down.
|
||||
minetest.sound_play(sound, {
|
||||
object = self.object,
|
||||
gain = 1.0,
|
||||
max_hear_distance = self.sounds.distance,
|
||||
pitch = pitch,
|
||||
}, true)
|
||||
self.opinion_sound_cooloff = 1
|
||||
end
|
||||
|
||||
function mob_class:step_opinion_sound(dtime)
|
||||
if self.state ~= "attack" and self.state ~= PATHFINDING then
|
||||
|
||||
if self.opinion_sound_cooloff > 0 then
|
||||
self.opinion_sound_cooloff = self.opinion_sound_cooloff - dtime
|
||||
end
|
||||
-- mob plays random sound at times. Should be 120. Zombie and mob farms are ridiculous
|
||||
if math.random(1, 70) == 1 then
|
||||
self:mob_sound("random", true)
|
||||
end
|
||||
if self.state == "attack" or self.state == PATHFINDING then return end
|
||||
if self.opinion_sound_cooloff > 0 then
|
||||
self.opinion_sound_cooloff = self.opinion_sound_cooloff - dtime
|
||||
end
|
||||
-- mob plays random sound at times. Should be 120. Zombie and mob farms are ridiculous
|
||||
if math.random(1, 70) == 1 then
|
||||
self:mob_sound("random", true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,33 +132,29 @@ function mob_class:remove_texture_mod(mod)
|
|||
table.insert(remove, i)
|
||||
end
|
||||
end
|
||||
for i=#remove, 1 do
|
||||
for i=#remove, 1, -1 do
|
||||
table.remove(self.texture_mods, remove[i])
|
||||
end
|
||||
self.object:set_texture_mod(full_mod)
|
||||
end
|
||||
|
||||
function mob_class:damage_effect(damage)
|
||||
-- damage particles
|
||||
if (not disable_blood) and damage > 0 then
|
||||
if disable_blood or damage <= 0 then return end
|
||||
local amount_large = math.floor(damage / 2)
|
||||
local amount_small = damage % 2
|
||||
|
||||
local amount_large = math.floor(damage / 2)
|
||||
local amount_small = damage % 2
|
||||
local pos = self.object:get_pos()
|
||||
pos.y = pos.y + (self.collisionbox[5] - self.collisionbox[2]) * .5
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
pos.y = pos.y + (self.collisionbox[5] - self.collisionbox[2]) * .5
|
||||
|
||||
local texture = "mobs_blood.png"
|
||||
-- full heart damage (one particle for each 2 HP damage)
|
||||
if amount_large > 0 then
|
||||
mcl_mobs.effect(pos, amount_large, texture, 2, 2, 1.75, 0, nil, true)
|
||||
end
|
||||
-- half heart damage (one additional particle if damage is an odd number)
|
||||
if amount_small > 0 then
|
||||
-- TODO: Use "half heart"
|
||||
mcl_mobs.effect(pos, amount_small, texture, 1, 1, 1.75, 0, nil, true)
|
||||
end
|
||||
local texture = "mobs_blood.png"
|
||||
-- full heart damage (one particle for each 2 HP damage)
|
||||
if amount_large > 0 then
|
||||
mcl_mobs.effect(pos, amount_large, texture, 2, 2, 1.75, 0, nil, true)
|
||||
end
|
||||
-- half heart damage (one additional particle if damage is an odd number)
|
||||
if amount_small > 0 then
|
||||
-- TODO: Use "half heart"
|
||||
mcl_mobs.effect(pos, amount_small, texture, 1, 1, 1.75, 0, nil, true)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -245,21 +204,15 @@ end
|
|||
|
||||
-- set defined animation
|
||||
function mob_class:set_animation(anim, fixed_frame)
|
||||
if not self.animation or not anim then
|
||||
return
|
||||
end
|
||||
if not self.animation or not anim then return end
|
||||
|
||||
if self.jockey and self.object:get_attach() then
|
||||
anim = "jockey"
|
||||
elseif not self.object:get_attach() then
|
||||
self.jockey = nil
|
||||
end
|
||||
|
||||
if self.state == "die" and anim ~= "die" and anim ~= "stand" then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if self.state == "die" and anim ~= "die" and anim ~= "stand" then return end
|
||||
|
||||
if self.fly and self:flight_check() and anim == "walk" then anim = "fly" end
|
||||
|
||||
|
@ -274,12 +227,7 @@ function mob_class:set_animation(anim, fixed_frame)
|
|||
self._current_animation = anim
|
||||
|
||||
local a_start = self.animation[anim .. "_start"]
|
||||
local a_end
|
||||
if fixed_frame then
|
||||
a_end = a_start
|
||||
else
|
||||
a_end = self.animation[anim .. "_end"]
|
||||
end
|
||||
local a_end = fixed_frame and a_start or self.animation[anim .. "_end"]
|
||||
if a_start and a_end then
|
||||
self.object:set_animation({
|
||||
x = a_start,
|
||||
|
@ -289,150 +237,135 @@ function mob_class:set_animation(anim, fixed_frame)
|
|||
end
|
||||
end
|
||||
|
||||
-- above function exported for mount.lua
|
||||
function mcl_mobs:set_animation(self, anim)
|
||||
self:set_animation(anim)
|
||||
end
|
||||
|
||||
local function dir_to_pitch(dir)
|
||||
--local dir2 = vector.normalize(dir)
|
||||
local xz = math.abs(dir.x) + math.abs(dir.z)
|
||||
return -math.atan2(-dir.y, xz)
|
||||
end
|
||||
|
||||
local function who_are_you_looking_at (self, dtime)
|
||||
local pos = self.object:get_pos()
|
||||
if self.order == "sleep" then
|
||||
self._locked_object = nil
|
||||
return
|
||||
end
|
||||
|
||||
local stop_look_at_player_chance = math.random(833/self.curiosity)
|
||||
-- was 10000 - div by 12 for avg entities as outside loop
|
||||
|
||||
local stop_look_at_player = stop_look_at_player_chance == 1
|
||||
local stop_look_at_player = math.random() * 833 <= self.curiosity
|
||||
|
||||
if self.attack then
|
||||
if not self.target_time_lost then
|
||||
self._locked_object = self.attack
|
||||
else
|
||||
self._locked_object = nil
|
||||
end
|
||||
self._locked_object = not self.target_time_lost and self.attack or nil
|
||||
elseif self.following then
|
||||
self._locked_object = self.following
|
||||
elseif self._locked_object then
|
||||
if stop_look_at_player then
|
||||
--minetest.log("Stop look: ".. self.name)
|
||||
self._locked_object = nil
|
||||
end
|
||||
if stop_look_at_player then self._locked_object = nil end
|
||||
elseif not self._locked_object then
|
||||
if mcl_util.check_dtime_timer(self, dtime, "step_look_for_someone", 0.2) then
|
||||
--minetest.log("Change look check: ".. self.name)
|
||||
|
||||
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
|
||||
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
|
||||
local chance = 150/self.curiosity
|
||||
|
||||
if chance < 1 then chance = 1 end
|
||||
local look_at_player_chance = math.random(chance)
|
||||
|
||||
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
|
||||
-- then div by 20 as less freq lookup
|
||||
|
||||
local look_at_player = look_at_player_chance == 1
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 8)) do
|
||||
if obj:is_player() and vector.distance(pos,obj:get_pos()) < 4 then
|
||||
--minetest.log("Change look to player: ".. self.name)
|
||||
if obj:is_player() and vector.distance(pos, obj:get_pos()) < 4 then
|
||||
self._locked_object = obj
|
||||
break
|
||||
elseif obj:is_player() or (obj:get_luaentity() and obj:get_luaentity().name == self.name and self ~= obj:get_luaentity()) then
|
||||
if look_at_player then
|
||||
--minetest.log("Change look to mob: ".. self.name)
|
||||
elseif obj:is_player() or (obj:get_luaentity() and self ~= obj:get_luaentity() and obj:get_luaentity().name == self.name) then
|
||||
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
|
||||
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
|
||||
-- was 5000 but called in loop based on entities. so div by 12 as estimate avg of entities found,
|
||||
-- then div by 20 as less freq lookup
|
||||
if math.random() * 150 <= self.curiosity then
|
||||
self._locked_object = obj
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function mob_class:check_head_swivel(dtime)
|
||||
if not self.head_swivel or type(self.head_swivel) ~= "string" then return end
|
||||
|
||||
who_are_you_looking_at(self, dtime)
|
||||
|
||||
who_are_you_looking_at (self, dtime)
|
||||
|
||||
local final_rotation = vector.zero()
|
||||
local oldp,oldr = self.object:get_bone_position(self.head_swivel)
|
||||
|
||||
if self._locked_object and (self._locked_object:is_player() or self._locked_object:get_luaentity()) and self._locked_object:get_hp() > 0 then
|
||||
local _locked_object_eye_height = 1.5
|
||||
if self._locked_object:get_luaentity() then
|
||||
_locked_object_eye_height = self._locked_object:get_luaentity().head_eye_height
|
||||
end
|
||||
if self._locked_object:is_player() then
|
||||
_locked_object_eye_height = self._locked_object:get_properties().eye_height
|
||||
end
|
||||
if _locked_object_eye_height then
|
||||
|
||||
local self_rot = self.object:get_rotation()
|
||||
-- If a mob is attached, should we really be messing with what they are looking at?
|
||||
-- Should this be excluded?
|
||||
if self.object:get_attach() and self.object:get_attach():get_rotation() then
|
||||
self_rot = self.object:get_attach():get_rotation()
|
||||
end
|
||||
|
||||
local player_pos = self._locked_object:get_pos()
|
||||
local direction_player = vector.direction(vector.add(self.object:get_pos(), vector.new(0, self.head_eye_height*.7, 0)), vector.add(player_pos, vector.new(0, _locked_object_eye_height, 0)))
|
||||
local mob_yaw = math.deg(-(-(self_rot.y)-(-minetest.dir_to_yaw(direction_player))))+self.head_yaw_offset
|
||||
local mob_pitch = math.deg(-dir_to_pitch(direction_player))*self.head_pitch_multiplier
|
||||
|
||||
if (mob_yaw < -60 or mob_yaw > 60) and not (self.attack and self.state == "attack" and not self.runaway) then
|
||||
final_rotation = vector.multiply(oldr, 0.9)
|
||||
elseif self.attack and self.state == "attack" and not self.runaway then
|
||||
if self.head_yaw == "y" then
|
||||
final_rotation = vector.new(mob_pitch, mob_yaw, 0)
|
||||
elseif self.head_yaw == "z" then
|
||||
final_rotation = vector.new(mob_pitch, 0, -mob_yaw)
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if self.head_yaw == "y" then
|
||||
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, ((mob_yaw-oldr.y)*.3)+oldr.y, 0)
|
||||
elseif self.head_yaw == "z" then
|
||||
final_rotation = vector.new(((mob_pitch-oldr.x)*.3)+oldr.x, 0, -(((mob_yaw-oldr.y)*.3)+oldr.y)*3)
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif not self._locked_object and math.abs(oldr.y) > 3 and math.abs(oldr.x) < 3 then
|
||||
final_rotation = vector.multiply(oldr, 0.9)
|
||||
else
|
||||
--final_rotation = vector.new(0,0,0)
|
||||
local newr = vector.zero()
|
||||
local oldp, oldr
|
||||
if self.object.get_bone_override then -- minetest >= 5.9
|
||||
local ov = self.object:get_bone_override(self.head_swivel)
|
||||
oldp, oldr = ov.position.vec, ov.rotation.vec
|
||||
else -- minetest < 5.9
|
||||
oldp, oldr = self.object:get_bone_position(self.head_swivel)
|
||||
oldr = vector.apply(oldr, math.rad) -- old API uses radians
|
||||
end
|
||||
|
||||
mcl_util.set_bone_position(self.object,self.head_swivel, vector.new(0,self.bone_eye_height,self.horizontal_head_height), final_rotation)
|
||||
local locked_object = self._locked_object
|
||||
if locked_object and (locked_object:is_player() or locked_object:get_luaentity()) and locked_object:get_hp() > 0 then
|
||||
local _locked_object_eye_height = (locked_object:is_player() and locked_object:get_properties().eye_height * 0.8) -- food in hands of player
|
||||
or (locked_object:get_luaentity() and locked_object:get_luaentity().head_eye_height) or 1.5
|
||||
local self_rot = self.object:get_rotation()
|
||||
-- If a mob is attached, should we really be messing with what they are looking at?
|
||||
-- Should this be excluded?
|
||||
if self.object:get_attach() and self.object:get_attach():get_rotation() then
|
||||
self_rot = self.object:get_attach():get_rotation()
|
||||
end
|
||||
|
||||
local ps = self.object:get_pos()
|
||||
ps.y = ps.y + self.head_eye_height -- why here, instead of below? * .7
|
||||
local pt = locked_object:get_pos()
|
||||
pt.y = pt.y + _locked_object_eye_height
|
||||
local dir = vector.direction(ps, pt) -- is (pt-ps):normalize()
|
||||
local mob_yaw = math.atan2(dir.x, dir.z)
|
||||
local mob_pitch = -math.asin(dir.y) * (self.head_pitch_multiplier or 1) -- allow axis inversion
|
||||
|
||||
mob_yaw = mob_yaw + self_rot.y -- to relative orientation
|
||||
while mob_yaw > PI do mob_yaw = mob_yaw - TWOPI end
|
||||
while mob_yaw < -PI do mob_yaw = mob_yaw + TWOPI end
|
||||
mob_yaw = mob_yaw * 0.8 -- lessen the effect so it become less staring
|
||||
local max_yaw = self.head_max_yaw or MAX_YAW
|
||||
mob_yaw = (mob_yaw < -max_yaw and -max_yaw) or (mob_yaw < max_yaw and mob_yaw) or max_yaw -- avoid twisting the neck
|
||||
|
||||
mob_pitch = mob_pitch * 0.8 -- make it less obvious that this is computed
|
||||
local max_pitch = self.head_max_pitch or MAX_PITCH
|
||||
mob_pitch = (mob_pitch < -max_pitch and -max_pitch) or (mob_pitch < max_pitch and mob_pitch) or max_pitch
|
||||
|
||||
local smoothing = (self.state == "attack" and self.attack and 0.25) or 0.05
|
||||
local old_pitch = oldr.x
|
||||
local old_yaw = (self.head_yaw == "y" and oldr.y or -oldr.z) - self.head_yaw_offset
|
||||
-- to -pi:+pi range, so we rotate over 0 when interpolating:
|
||||
while old_yaw > PI do old_yaw = old_yaw - TWOPI end
|
||||
while old_yaw < -PI do old_yaw = old_yaw + TWOPI end
|
||||
mob_pitch, mob_yaw = (mob_pitch-old_pitch)*smoothing+old_pitch, (mob_yaw-old_yaw)*smoothing+old_yaw
|
||||
-- apply the yaw to the mob
|
||||
mob_yaw = mob_yaw + self.head_yaw_offset
|
||||
if self.head_yaw == "y" then
|
||||
newr = vector.new(mob_pitch, mob_yaw, 0)
|
||||
elseif self.head_yaw == "z" then
|
||||
newr = vector.new(mob_pitch, 0, -mob_yaw) -- z yaw is opposite direction
|
||||
end
|
||||
elseif math.abs(oldr.x) + math.abs(oldr.y) + math.abs(oldr.z) > 0.05 then
|
||||
newr = vector.multiply(oldr, 0.9) -- smooth stop looking
|
||||
end
|
||||
|
||||
-- 0.02 is about 1.14 degrees tolerance, to update less often
|
||||
if math.abs(oldr.x-newr.x) + math.abs(oldr.y-newr.y) + math.abs(oldr.z-newr.z) < 0.02 then return end
|
||||
|
||||
if self.object.get_bone_override then -- minetest >= 5.9
|
||||
self.object:set_bone_override(self.head_swivel, {
|
||||
--position = { vec = vector.zero(), absolute = false },
|
||||
rotation = { vec = newr, absolute = true, interpolation = 0.1 } })
|
||||
else -- minetest < 5.9
|
||||
-- old API uses degrees not radians and absolute positions
|
||||
self.object:set_bone_position(self.head_swivel, self.head_bone_position, vector.apply(newr, math.deg))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function mob_class:set_animation_speed()
|
||||
local v = self.object:get_velocity()
|
||||
if v then
|
||||
local v = self:get_velocity()
|
||||
if v > 0 then
|
||||
if self.frame_speed_multiplier then
|
||||
local v2 = math.abs(v.x)+math.abs(v.z)*.833
|
||||
if not self.animation.walk_speed then
|
||||
self.animation.walk_speed = 25
|
||||
end
|
||||
if math.abs(v.x)+math.abs(v.z) > 0.5 then
|
||||
self.object:set_animation_frame_speed((v2/math.max(1,self.run_velocity))*self.animation.walk_speed*self.frame_speed_multiplier)
|
||||
self.animation.walk_speed = self.animation.walk_speed or 25 -- TODO: move to initialization
|
||||
if v > 0.5 then
|
||||
self.object:set_animation_frame_speed((v/math.max(1,self.run_velocity))*self.animation.walk_speed*self.frame_speed_multiplier)
|
||||
else
|
||||
self.object:set_animation_frame_speed(25)
|
||||
end
|
||||
end
|
||||
--set_speed
|
||||
if validate_vector(self.acc) then
|
||||
self.object:add_velocity(self.acc)
|
||||
end
|
||||
--if validate_vector(self.acc) then
|
||||
-- self.object:add_velocity(self.acc)
|
||||
--end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,6 +6,40 @@ local modname = minetest.get_current_modname()
|
|||
local path = minetest.get_modpath(modname)
|
||||
local S = minetest.get_translator(modname)
|
||||
mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
||||
mcl_mobs.see_through_opaque = minetest.settings:get_bool("mobs_see_through_opaque", false)
|
||||
|
||||
-- used by the libaries below.
|
||||
-- get node but use fallback for nil or unknown
|
||||
local function node_ok(pos, fallback)
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
mcl_mobs.node_ok = node_ok
|
||||
|
||||
local function line_of_sight(origin, target, see_through_opaque, liquids)
|
||||
local raycast = minetest.raycast(origin, target, false, liquids or false)
|
||||
for hitpoint in raycast do
|
||||
if hitpoint.type == "node" then
|
||||
local node = minetest.get_node(minetest.get_pointed_thing_position(hitpoint))
|
||||
if node.name ~= "air" then
|
||||
local nodef = minetest.registered_nodes[node.name]
|
||||
if nodef and nodef.walkable and not (see_through_opaque and not nodef.groups.opaque) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
--TODO type object could block vision, for example chests
|
||||
end
|
||||
return true
|
||||
end
|
||||
mcl_mobs.line_of_sight = line_of_sight
|
||||
|
||||
mcl_mobs.NODE_IGNORE = { name = "ignore", groups = {} } -- fallback for unknown nodes
|
||||
|
||||
--api and helpers
|
||||
-- effects: sounds and particles mostly
|
||||
dofile(path .. "/effects.lua")
|
||||
|
@ -19,10 +53,9 @@ dofile(path .. "/items.lua")
|
|||
dofile(path .. "/pathfinding.lua")
|
||||
-- combat: attack logic
|
||||
dofile(path .. "/combat.lua")
|
||||
-- the enity functions themselves
|
||||
-- the entity functions themselves
|
||||
dofile(path .. "/api.lua")
|
||||
|
||||
|
||||
--utility functions
|
||||
dofile(path .. "/breeding.lua")
|
||||
dofile(path .. "/spawning.lua")
|
||||
|
@ -37,16 +70,6 @@ local old_spawn_icons = minetest.settings:get_bool("mcl_old_spawn_icons",false)
|
|||
local extended_pet_control = minetest.settings:get_bool("mcl_extended_pet_control",true)
|
||||
local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0
|
||||
|
||||
-- get node but use fallback for nil or unknown
|
||||
local node_ok = function(pos, fallback)
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
return minetest.registered_nodes[fallback]
|
||||
end
|
||||
|
||||
--#### REGISTER FUNCS
|
||||
|
||||
-- Code to execute before custom on_rightclick handling
|
||||
|
@ -114,14 +137,8 @@ function mcl_mobs.register_mob(name, def)
|
|||
mcl_mobs.spawning_mobs[name] = true
|
||||
mcl_mobs.registered_mobs[name] = def
|
||||
|
||||
local can_despawn
|
||||
if def.can_despawn ~= nil then
|
||||
can_despawn = def.can_despawn
|
||||
elseif def.spawn_class == "passive" then
|
||||
can_despawn = false
|
||||
else
|
||||
can_despawn = true
|
||||
end
|
||||
local can_despawn = def.can_despawn
|
||||
if def.can_despawn == nil then can_despawn = def.spawn_class ~= "passive" end
|
||||
|
||||
local function scale_difficulty(value, default, min, special)
|
||||
if (not value) or (value == default) or (value == special) then
|
||||
|
@ -131,23 +148,29 @@ function mcl_mobs.register_mob(name, def)
|
|||
end
|
||||
end
|
||||
|
||||
if type(def.fly_in) == "string" then
|
||||
def.fly_in = { def.fly_in }
|
||||
end
|
||||
|
||||
local collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}
|
||||
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
|
||||
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
|
||||
-- Removed now, as this was supposedly fixed in 5.3.0?
|
||||
-- FIXME: Remove workaround if it's no longer needed.
|
||||
if collisionbox[5] < 0.79 then
|
||||
collisionbox[5] = 0.79
|
||||
end
|
||||
--if collisionbox[5] < 0.79 then
|
||||
-- collisionbox[5] = 0.79
|
||||
--end
|
||||
local final_def = {
|
||||
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
|
||||
head_yaw_offset = math.rad(def.head_yaw_offset or 0), -- for wonkey model bones
|
||||
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
|
||||
bone_eye_height = def.bone_eye_height or 1.4, -- head bone offset
|
||||
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how hight aproximatly the mobs head is fromm the ground to tell the mob how high to look up at the player
|
||||
head_eye_height = def.head_eye_height or 1, -- how high approximately the mobs eyes are from the ground to tell the mob how high to look up at the player
|
||||
head_max_yaw = def.head_max_yaw, -- how far the mob may turn the head
|
||||
head_max_pitch = def.head_max_pitch, -- how far up and down the mob may pitch the head
|
||||
head_bone_position = def.head_bone_position or { 0, def.bone_eye_height or 1.4, def.horizontal_head_height or 0},
|
||||
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
||||
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
||||
horizontal_head_height = def.horizontal_head_height or 0,
|
||||
wears_armor = def.wears_armor, -- a number value used to index texture slot for armor
|
||||
stepheight = def.stepheight or 0.6,
|
||||
name = name,
|
||||
|
@ -163,7 +186,7 @@ function mcl_mobs.register_mob(name, def)
|
|||
spawn_small_alternative = def.spawn_small_alternative,
|
||||
do_custom = def.do_custom,
|
||||
detach_child = def.detach_child,
|
||||
jump_height = def.jump_height or 4, -- was 6
|
||||
jump_height = def.jump_height or 1,
|
||||
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
|
||||
lifetimer = def.lifetimer or 57.73,
|
||||
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
||||
|
@ -229,7 +252,6 @@ function mcl_mobs.register_mob(name, def)
|
|||
health = 0,
|
||||
frame_speed_multiplier = 1,
|
||||
reach = def.reach or 3,
|
||||
htimer = 0,
|
||||
texture_list = def.textures,
|
||||
child_texture = def.child_texture,
|
||||
docile_by_day = def.docile_by_day or false,
|
||||
|
@ -383,11 +405,8 @@ end
|
|||
|
||||
-- register arrow for shoot attack
|
||||
function mcl_mobs.register_arrow(name, def)
|
||||
|
||||
if not name or not def then return end -- errorcheck
|
||||
|
||||
minetest.register_entity(name, {
|
||||
|
||||
physical = false,
|
||||
visual = def.visual,
|
||||
visual_size = def.visual_size,
|
||||
|
@ -399,7 +418,6 @@ function mcl_mobs.register_arrow(name, def)
|
|||
hit_object = def.hit_object,
|
||||
homing = def.homing,
|
||||
drop = def.drop or false, -- drops arrow as registered item when true
|
||||
collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows
|
||||
timer = 0,
|
||||
switch = 0,
|
||||
_lifetime = def._lifetime or 7,
|
||||
|
@ -411,31 +429,21 @@ function mcl_mobs.register_arrow(name, def)
|
|||
self._puncher = puncher
|
||||
end,
|
||||
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
||||
automatic_face_movement_dir = def.rotate
|
||||
and (def.rotate - (math.pi / 180)) or false,
|
||||
automatic_face_movement_dir = def.rotate and (def.rotate - (math.pi / 180)) or false,
|
||||
|
||||
on_activate = def.on_activate,
|
||||
|
||||
on_step = def.on_step or function(self, dtime)
|
||||
|
||||
self.timer = self.timer + dtime
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
if self.switch == 0
|
||||
or self.timer > self._lifetime
|
||||
or not within_limits(pos, 0) then
|
||||
if self.switch == 0 or self.timer > self._lifetime or not within_limits(pos, 0) then
|
||||
mcl_burning.extinguish(self.object)
|
||||
self.object:remove();
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- does arrow have a tail (fireball)
|
||||
if def.tail
|
||||
and def.tail == 1
|
||||
and def.tail_texture then
|
||||
|
||||
if def.tail == 1 and def.tail_texture then
|
||||
minetest.add_particle({
|
||||
pos = pos,
|
||||
velocity = {x = 0, y = 0, z = 0},
|
||||
|
@ -449,24 +457,17 @@ function mcl_mobs.register_arrow(name, def)
|
|||
end
|
||||
|
||||
if self.hit_node then
|
||||
|
||||
local node = node_ok(pos).name
|
||||
|
||||
if minetest.registered_nodes[node].walkable then
|
||||
|
||||
self.hit_node(self, pos, node)
|
||||
|
||||
if self.drop == true then
|
||||
|
||||
pos.y = pos.y + 1
|
||||
|
||||
self.lastpos = (self.lastpos or pos)
|
||||
|
||||
minetest.add_item(self.lastpos, self.object:get_luaentity().name)
|
||||
end
|
||||
|
||||
self.object:remove();
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -483,12 +484,8 @@ function mcl_mobs.register_arrow(name, def)
|
|||
end
|
||||
|
||||
if self.hit_player or self.hit_mob or self.hit_object then
|
||||
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
||||
|
||||
if self.hit_player
|
||||
and object:is_player() then
|
||||
|
||||
if self.hit_player and object:is_player() then
|
||||
self.hit_player(self, object)
|
||||
self.object:remove();
|
||||
return
|
||||
|
@ -529,30 +526,20 @@ end
|
|||
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
||||
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
|
||||
function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, addegg, no_creative)
|
||||
|
||||
local grp = {spawn_egg = 1}
|
||||
|
||||
-- do NOT add this egg to creative inventory (e.g. dungeon master)
|
||||
if no_creative == true then
|
||||
grp.not_in_creative_inventory = 1
|
||||
end
|
||||
local grp = { spawn_egg = 1 }
|
||||
if no_creative == true then grp.not_in_creative_inventory = 1 end
|
||||
|
||||
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
|
||||
if old_spawn_icons then
|
||||
local mobname = mob_id:gsub("mobs_mc:","")
|
||||
local fn = "mobs_mc_spawn_icon_"..mobname..".png"
|
||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
|
||||
invimg = fn
|
||||
end
|
||||
local fn = "mobs_mc_spawn_icon_" .. mob_id:gsub("mobs_mc:","") .. ".png"
|
||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then invimg = fn end
|
||||
end
|
||||
if addegg == 1 then
|
||||
invimg = "mobs_chicken_egg.png^(" .. invimg ..
|
||||
"^[mask:mobs_chicken_egg_overlay.png)"
|
||||
invimg = "mobs_chicken_egg.png^(" .. invimg .. "^[mask:mobs_chicken_egg_overlay.png)"
|
||||
end
|
||||
|
||||
-- register old stackable mob egg
|
||||
minetest.register_craftitem(mob_id, {
|
||||
|
||||
description = desc,
|
||||
inventory_image = invimg,
|
||||
groups = grp,
|
||||
|
@ -589,9 +576,6 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
|
|||
local dim = mcl_worlds.pos_to_dimension(placer:get_pos())
|
||||
local mob_light_lvl = {mcl_mobs:mob_light_lvl(itemstack:get_name(),dim)}
|
||||
|
||||
--minetest.log("min light: " .. mob_light_lvl[1])
|
||||
--minetest.log("max light: " .. mob_light_lvl[2])
|
||||
|
||||
-- Handle egg conversion
|
||||
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
|
||||
if convert_to then mob_name = convert_to end
|
||||
|
@ -603,9 +587,7 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
|
|||
return itemstack
|
||||
end
|
||||
|
||||
if not minetest.registered_entities[mob_name] then
|
||||
return itemstack
|
||||
end
|
||||
if not minetest.registered_entities[mob_name] then return itemstack end
|
||||
|
||||
if minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||
and minetest.registered_entities[mob_name].type == "monster" then
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
local minetest, mcl_mobs = minetest, mcl_mobs
|
||||
local mob_class = mcl_mobs.mob_class
|
||||
--- Item and armor management
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = mcl_mobs
|
||||
author = PilzAdam
|
||||
author = PilzAdam, kno10
|
||||
description = Adds a mob API for mods to add animals or monsters, etc.
|
||||
depends = mcl_particles, mcl_luck
|
||||
optional_depends = mcl_weather, mcl_explosions, mcl_hunger, mcl_worlds, invisibility, lucky_block, cmi, doc_identifier, mcl_armor, mcl_portals, mcl_experience, mcl_sculk
|
||||
|
|
|
@ -1,110 +1,41 @@
|
|||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
local mob_class = mcl_mobs.mob_class
|
||||
-- lib_mount by Blert2112 (edited by TenPlus1)
|
||||
-- based on lib_mount by Blert2112 (edited by TenPlus1)
|
||||
|
||||
local enable_crash = false
|
||||
local crash_threshold = 6.5 -- ignored if enable_crash=false
|
||||
local GRAVITY = -9.8
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Helper functions
|
||||
--
|
||||
|
||||
local node_ok = function(pos, fallback)
|
||||
|
||||
fallback = fallback or mcl_mobs.fallback_node
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
|
||||
return {name = fallback}
|
||||
end
|
||||
|
||||
local node_ok = mcl_mobs.node_ok
|
||||
local sign = math.sign -- minetest extension
|
||||
|
||||
local function node_is(pos)
|
||||
|
||||
local node = node_ok(pos)
|
||||
|
||||
if node.name == "air" then
|
||||
return "air"
|
||||
end
|
||||
|
||||
if minetest.get_item_group(node.name, "lava") ~= 0 then
|
||||
return "lava"
|
||||
end
|
||||
|
||||
if minetest.get_item_group(node.name, "liquid") ~= 0 then
|
||||
return "liquid"
|
||||
end
|
||||
|
||||
if minetest.registered_nodes[node.name].walkable == true then
|
||||
return "walkable"
|
||||
end
|
||||
|
||||
if node.name == "air" then return "air" end
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if not ndef then return "other" end -- unknown/ignore
|
||||
if ndef.groups.lava then return "lava" end
|
||||
if ndef.groups.liquid then return "liquid" end
|
||||
if ndef.walkable then return "walkable" end
|
||||
return "other"
|
||||
end
|
||||
|
||||
|
||||
local function get_sign(i)
|
||||
|
||||
i = i or 0
|
||||
|
||||
if i == 0 then
|
||||
return 0
|
||||
else
|
||||
return i / math.abs(i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_velocity(v, yaw, y)
|
||||
|
||||
local x = -math.sin(yaw) * v
|
||||
local z = math.cos(yaw) * v
|
||||
|
||||
return {x = x, y = y, z = z}
|
||||
end
|
||||
|
||||
|
||||
local function get_v(v)
|
||||
return math.sqrt(v.x * v.x + v.z * v.z)
|
||||
end
|
||||
|
||||
|
||||
local function force_detach(player)
|
||||
|
||||
local attached_to = player:get_attach()
|
||||
|
||||
if not attached_to then
|
||||
return
|
||||
end
|
||||
if not attached_to then return end
|
||||
|
||||
local entity = attached_to:get_luaentity()
|
||||
|
||||
if entity.driver
|
||||
and entity.driver == player then
|
||||
|
||||
entity.driver = nil
|
||||
end
|
||||
if entity.driver and entity.driver == player then entity.driver = nil end
|
||||
|
||||
player:set_detach()
|
||||
mcl_player.player_attached[player:get_player_name()] = false
|
||||
player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
|
||||
player:set_eye_offset(vector.zero(), vector.zero())
|
||||
mcl_player.player_set_animation(player, "stand" , 30)
|
||||
player:set_properties({visual_size = {x = 1, y = 1} })
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
force_detach(player)
|
||||
end)
|
||||
minetest.register_on_leaveplayer(force_detach)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
local players = minetest.get_connected_players()
|
||||
|
@ -118,39 +49,24 @@ minetest.register_on_dieplayer(function(player)
|
|||
return true
|
||||
end)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function mcl_mobs.attach(entity, player)
|
||||
|
||||
local attach_at, eye_offset
|
||||
|
||||
entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
|
||||
entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
|
||||
entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
|
||||
entity.player_rotation = entity.player_rotation or vector.zero()
|
||||
entity.driver_attach_at = entity.driver_attach_at or vector.zero()
|
||||
entity.driver_eye_offset = entity.driver_eye_offset or vector.zero()
|
||||
entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
|
||||
|
||||
local rot_view = 0
|
||||
|
||||
if entity.player_rotation.y == 90 then
|
||||
rot_view = math.pi/2
|
||||
end
|
||||
|
||||
attach_at = entity.driver_attach_at
|
||||
eye_offset = entity.driver_eye_offset
|
||||
local rot_view = entity.player_rotation.y == 90 and math.pi/2 or 0
|
||||
local attach_at = entity.driver_attach_at
|
||||
local eye_offset = entity.driver_eye_offset
|
||||
entity.driver = player
|
||||
|
||||
force_detach(player)
|
||||
|
||||
player:set_attach(entity.object, "", attach_at, entity.player_rotation)
|
||||
mcl_player.player_attached[player:get_player_name()] = true
|
||||
player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
|
||||
player:set_eye_offset(eye_offset, vector.zero())
|
||||
|
||||
player:set_properties({
|
||||
visual_size = {
|
||||
x = entity.driver_scale.x,
|
||||
y = entity.driver_scale.y
|
||||
}
|
||||
})
|
||||
player:set_properties({ visual_size = entity.driver_scale })
|
||||
|
||||
minetest.after(0.2, function(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
@ -164,162 +80,88 @@ end
|
|||
|
||||
|
||||
function mcl_mobs.detach(player, offset)
|
||||
|
||||
force_detach(player)
|
||||
|
||||
mcl_player.player_set_animation(player, "stand" , 30)
|
||||
|
||||
--local pos = player:get_pos()
|
||||
|
||||
--pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
|
||||
|
||||
player:add_velocity(vector.new(math.random(-6,6),math.random(5,8),math.random(-6,6))) --throw the rider off
|
||||
|
||||
--[[
|
||||
minetest.after(0.1, function(name, pos)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
player:set_pos(pos)
|
||||
end
|
||||
end, player:get_player_name(), pos)
|
||||
]]--
|
||||
player:add_velocity(vector.new(math.random()*12-6,math.random()*3+5,math.random()*12-6)) --throw the rider off
|
||||
end
|
||||
|
||||
|
||||
function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
||||
|
||||
local rot_view = 0
|
||||
|
||||
if entity.player_rotation.y == 90 then
|
||||
rot_view = math.pi/2
|
||||
end
|
||||
|
||||
local acce_y = 0
|
||||
local velo = entity.object:get_velocity()
|
||||
|
||||
entity.v = get_v(velo) * get_sign(entity.v)
|
||||
local v = math.sqrt(velo.x * velo.x + velo.y * velo.y)
|
||||
local acce_y = GRAVITY
|
||||
|
||||
-- process controls
|
||||
if entity.driver then
|
||||
|
||||
local ctrl = entity.driver:get_player_control()
|
||||
|
||||
-- move forwards
|
||||
if ctrl.up then
|
||||
|
||||
entity.v = entity.v + entity.accel / 10 * entity.run_velocity / 2.6
|
||||
|
||||
-- move backwards
|
||||
elseif ctrl.down then
|
||||
|
||||
if entity.max_speed_reverse == 0 and entity.v == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
entity.v = entity.v - entity.accel / 10
|
||||
if ctrl.up then -- forward
|
||||
v = v + entity.accel * 0.1 * entity.run_velocity * 0.385
|
||||
elseif ctrl.down then -- backwards
|
||||
if entity.max_speed_reverse == 0 and v == 0 then return end
|
||||
v = v - entity.accel * 0.1 * entity.run_velocity * 0.385
|
||||
end
|
||||
|
||||
-- fix mob rotation
|
||||
entity.object:set_yaw(entity.driver:get_look_horizontal() - entity.rotate)
|
||||
entity:set_yaw(entity.driver:get_look_horizontal() - entity.rotate, 2)
|
||||
|
||||
if can_fly then
|
||||
|
||||
-- FIXME: use acce_y instead?
|
||||
-- fly up
|
||||
if ctrl.jump then
|
||||
velo.y = velo.y + 1
|
||||
if velo.y > entity.accel then velo.y = entity.accel end
|
||||
|
||||
elseif velo.y > 0 then
|
||||
velo.y = math.min(velo.y + 1, entity.accel)
|
||||
elseif velo.y > 0.1 then
|
||||
velo.y = velo.y - 0.1
|
||||
if velo.y < 0 then velo.y = 0 end
|
||||
elseif velo.y > 0 then
|
||||
velo.y = 0
|
||||
end
|
||||
|
||||
-- fly down
|
||||
if ctrl.sneak then
|
||||
velo.y = velo.y - 1
|
||||
if velo.y < -entity.accel then velo.y = -entity.accel end
|
||||
|
||||
elseif velo.y < 0 then
|
||||
velo.y = math.max(velo.y - 1, -entity.accel)
|
||||
elseif velo.y < -0.1 then
|
||||
velo.y = velo.y + 0.1
|
||||
if velo.y > 0 then velo.y = 0 end
|
||||
elseif velo.y < 0 then
|
||||
velo.y = 0
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- jump
|
||||
if ctrl.jump then
|
||||
|
||||
if velo.y == 0 then
|
||||
velo.y = velo.y + entity.jump_height
|
||||
acce_y = acce_y + (acce_y * 3) + 1
|
||||
velo.y = velo.y + math.sqrt(entity.jump_height * 20)
|
||||
acce_y = acce_y + 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Stop!
|
||||
local s = get_sign(entity.v)
|
||||
|
||||
entity.v = entity.v - 0.02 * s
|
||||
|
||||
if s ~= get_sign(entity.v) then
|
||||
|
||||
entity.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
entity.v = 0
|
||||
return
|
||||
if math.abs(v) < 0.02 then -- stop
|
||||
entity.object:set_velocity(vector.zero())
|
||||
v = 0
|
||||
else
|
||||
v = v - 0.02 * sign(v) -- slow down
|
||||
end
|
||||
|
||||
-- if not moving then set animation and return
|
||||
if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
|
||||
if stand_anim then
|
||||
mcl_mobs:set_animation(entity, stand_anim)
|
||||
end
|
||||
|
||||
if v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
entity:set_animation(stand_anim)
|
||||
return
|
||||
end
|
||||
|
||||
-- set moving animation
|
||||
if moving_anim then
|
||||
mcl_mobs:set_animation(entity, moving_anim)
|
||||
else
|
||||
entity:set_animation(moving_anim)
|
||||
end
|
||||
|
||||
-- enforce speed limit forward and reverse
|
||||
local max_spd = entity.max_speed_reverse
|
||||
|
||||
if get_sign(entity.v) >= 0 then
|
||||
max_spd = entity.max_speed_forward
|
||||
end
|
||||
|
||||
if math.abs(entity.v) > max_spd then
|
||||
entity.v = entity.v - get_sign(entity.v)
|
||||
end
|
||||
v = math.max(-entity.max_speed_reverse, math.min(v, entity.max_speed_forward))
|
||||
|
||||
-- Set position, velocity and acceleration
|
||||
local p = entity.object:get_pos()
|
||||
local new_velo
|
||||
local new_acce = {x = 0, y = -9.8, z = 0}
|
||||
|
||||
p.y = p.y - 0.5
|
||||
|
||||
local ni = node_is(p)
|
||||
local v = entity.v
|
||||
|
||||
if ni == "air" then
|
||||
|
||||
if can_fly == true then
|
||||
new_acce.y = 0
|
||||
end
|
||||
|
||||
if can_fly then acce_y = acce_y - GRAVITY end
|
||||
elseif ni == "liquid" or ni == "lava" then
|
||||
|
||||
if ni == "lava" and entity.lava_damage ~= 0 then
|
||||
|
||||
entity.lava_counter = (entity.lava_counter or 0) + dtime
|
||||
|
||||
if entity.lava_counter > 1 then
|
||||
|
||||
minetest.sound_play("default_punch", {
|
||||
object = entity.object,
|
||||
max_hear_distance = 5
|
||||
|
@ -336,18 +178,15 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
|||
|
||||
if entity.terrain_type == 2
|
||||
or entity.terrain_type == 3 then
|
||||
|
||||
new_acce.y = 0
|
||||
acce_y = 0
|
||||
p.y = p.y + 1
|
||||
|
||||
if node_is(p) == "liquid" then
|
||||
|
||||
if velo.y >= 5 then
|
||||
velo.y = 5
|
||||
elseif velo.y < 0 then
|
||||
new_acce.y = 20
|
||||
acce_y = 20
|
||||
else
|
||||
new_acce.y = 5
|
||||
acce_y = 5
|
||||
end
|
||||
else
|
||||
if math.abs(velo.y) < 1 then
|
||||
|
@ -362,75 +201,51 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
|
|||
end
|
||||
end
|
||||
|
||||
new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y)
|
||||
new_acce.y = new_acce.y + acce_y
|
||||
local rot_view = entity.player_rotation.y == 90 and math.pi/2 or 0
|
||||
local new_yaw = entity.object:get_yaw() - rot_view
|
||||
local new_velo = vector.new(-math.sin(new_yaw) * v, velo.y, math.cos(new_yaw) * v)
|
||||
|
||||
entity.object:set_velocity(new_velo)
|
||||
entity.object:set_acceleration(new_acce)
|
||||
entity.object:set_acceleration(vector.new(0, acce_y, 0))
|
||||
|
||||
-- CRASH!
|
||||
if enable_crash then
|
||||
|
||||
local intensity = entity.v2 - v
|
||||
|
||||
if intensity >= crash_threshold then
|
||||
|
||||
if v >= crash_threshold then
|
||||
entity.object:punch(entity.object, 1.0, {
|
||||
full_punch_interval = 1.0,
|
||||
damage_groups = {fleshy = intensity}
|
||||
damage_groups = {fleshy = v}
|
||||
}, nil)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
entity.v2 = v
|
||||
end
|
||||
|
||||
-- directional flying routine by D00Med (edited by TenPlus1)
|
||||
|
||||
function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
|
||||
|
||||
local ctrl = entity.driver:get_player_control()
|
||||
local velo = entity.object:get_velocity()
|
||||
local dir = entity.driver:get_look_dir()
|
||||
local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
|
||||
local yaw = entity.driver:get_look_horizontal()
|
||||
|
||||
if ctrl.up then
|
||||
entity.object:set_velocity({
|
||||
x = dir.x * speed,
|
||||
y = dir.y * speed + 2,
|
||||
z = dir.z * speed
|
||||
})
|
||||
|
||||
entity.object:set_velocity(vector.new(dir.x * speed, dir.y * speed + 2, dir.z * speed))
|
||||
elseif ctrl.down then
|
||||
entity.object:set_velocity({
|
||||
x = -dir.x * speed,
|
||||
y = dir.y * speed + 2,
|
||||
z = -dir.z * speed
|
||||
})
|
||||
|
||||
entity.object:set_velocity(vector.new(-dir.x * speed, dir.y * speed + 2, -dir.z * speed))
|
||||
elseif not ctrl.down or ctrl.up or ctrl.jump then
|
||||
entity.object:set_velocity({x = 0, y = -2, z = 0})
|
||||
entity.object:set_velocity(vector.new(0, -2, 0))
|
||||
end
|
||||
|
||||
entity.object:set_yaw(yaw + math.pi + math.pi / 2 - entity.rotate)
|
||||
entity:set_yaw(yaw - entity.rotate, 2)
|
||||
|
||||
-- firing arrows
|
||||
if ctrl.LMB and ctrl.sneak and shoots then
|
||||
|
||||
local pos = entity.object:get_pos()
|
||||
local obj = minetest.add_entity({
|
||||
x = pos.x + 0 + dir.x * 2.5,
|
||||
y = pos.y + 1.5 + dir.y,
|
||||
z = pos.z + 0 + dir.z * 2.5}, arrow)
|
||||
|
||||
local obj = minetest.add_entity(vector.offset(pos, dir.x * 2.5, 1.5 + dir.y, dir.z * 2.5), arrow)
|
||||
local ent = obj:get_luaentity()
|
||||
if ent then
|
||||
ent.switch = 1 -- for mob specific arrows
|
||||
ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
|
||||
local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
|
||||
local vec = vector.new(dir.x * 6, dir.y * 6, dir.z * 6)
|
||||
local yaw = entity.driver:get_look_horizontal()
|
||||
obj:set_yaw(yaw + math.pi / 2)
|
||||
obj:set_yaw(yaw)
|
||||
obj:set_velocity(vec)
|
||||
else
|
||||
obj:remove()
|
||||
|
@ -439,11 +254,9 @@ function mcl_mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_an
|
|||
|
||||
-- change animation if stopped
|
||||
if velo.x == 0 and velo.y == 0 and velo.z == 0 then
|
||||
|
||||
mcl_mobs:set_animation(entity, stand_anim)
|
||||
entity:set_animation(stand_anim)
|
||||
else
|
||||
-- moving animation
|
||||
mcl_mobs:set_animation(entity, moving_anim)
|
||||
entity:set_animation(moving_anim)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -452,12 +265,7 @@ mcl_mobs.mob_class.fly = mcl_mobs.fly
|
|||
mcl_mobs.mob_class.attach = mcl_mobs.attach
|
||||
|
||||
function mob_class:on_detach_child(child)
|
||||
if self.detach_child then
|
||||
if self.detach_child(self, child) then
|
||||
return
|
||||
end
|
||||
end
|
||||
if self.driver == child then
|
||||
self.driver = nil
|
||||
end
|
||||
if self.detach_child and self.detach_child(self, child) then return end
|
||||
if self.driver == child then self.driver = nil end
|
||||
end
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,17 +1,14 @@
|
|||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
local 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 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 one_down = vector.new(0,-1,0)
|
||||
local one_up = vector.new(0,1,0)
|
||||
|
||||
local plane_adjacents = {
|
||||
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 visualize = minetest.settings:get_bool("mcl_mobs_pathfinding_visualize",false)
|
||||
|
||||
local LOG_MODULE = "[Mobs Pathfinding]"
|
||||
local function mcl_log (message)
|
||||
|
@ -42,8 +40,8 @@ function append_paths (wp1, wp2)
|
|||
mcl_log("Cannot append wp's")
|
||||
return
|
||||
end
|
||||
output_table(wp1)
|
||||
output_table(wp2)
|
||||
--output_table(wp1)
|
||||
--output_table(wp2)
|
||||
for _,a in pairs (wp2) do
|
||||
table.insert(wp1, a)
|
||||
end
|
||||
|
@ -51,18 +49,13 @@ function append_paths (wp1, wp2)
|
|||
end
|
||||
|
||||
local function output_enriched (wp_out)
|
||||
mcl_log("Output enriched path")
|
||||
--mcl_log("Output enriched path")
|
||||
local i = 0
|
||||
for _,outy in pairs (wp_out) do
|
||||
i = i + 1
|
||||
mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
||||
|
||||
local action = outy["action"]
|
||||
if action then
|
||||
--mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"]))
|
||||
mcl_log("type: " .. action["type"])
|
||||
mcl_log("action: " .. action["action"])
|
||||
mcl_log("target: " .. minetest.pos_to_string(action["target"]))
|
||||
mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])..", type: " .. action["type"]..", action: " .. action["action"]..", target: " .. minetest.pos_to_string(action["target"]))
|
||||
end
|
||||
--mcl_log("failed attempts: " .. outy["failed_attempts"])
|
||||
end
|
||||
|
@ -73,33 +66,22 @@ end
|
|||
-- 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 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
|
||||
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")
|
||||
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
|
||||
mcl_log ("Door close match")
|
||||
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
|
||||
mcl_log("Current 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
|
||||
|
||||
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]["action"] = action
|
||||
|
||||
|
@ -113,93 +95,82 @@ end
|
|||
local last_pathing_time = os.time()
|
||||
|
||||
function mob_class:ready_to_path(prioritised)
|
||||
mcl_log("Check ready to path")
|
||||
-- mcl_log("Check ready to path")
|
||||
if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then
|
||||
mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
||||
-- mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed))
|
||||
return false
|
||||
else
|
||||
local time_since_path_start = os.time() - last_pathing_time
|
||||
mcl_log("time_since_path_start: " .. tostring(time_since_path_start))
|
||||
if prioritised or (time_since_path_start) > PATHING_START_DELAY then
|
||||
mcl_log("We are ready to pathfind, no previous fail or we are past threshold")
|
||||
mcl_log("We are ready to pathfind, no previous fail or we are past threshold: "..tostring(time_since_path_start))
|
||||
return true
|
||||
end
|
||||
mcl_log("time_since_path_start: " .. tostring(time_since_path_start))
|
||||
end
|
||||
end
|
||||
|
||||
-- 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)
|
||||
if not cur_door_pos then return end
|
||||
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
|
||||
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
|
||||
|
||||
local enriched_path = nil
|
||||
local wp, prospective_wp
|
||||
for _,v in pairs(plane_adjacents) do
|
||||
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 other_side_of_door = nil
|
||||
local prospective_wp = minetest.find_path(p, pos_closest_to_door, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||
|
||||
if cur_door_pos then
|
||||
mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos))
|
||||
if prospective_wp then
|
||||
local other_side_of_door = vector.add(cur_door_pos,-v)
|
||||
mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door))
|
||||
mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door))
|
||||
|
||||
for _,v in pairs(plane_adjacents) do
|
||||
pos_closest_to_door = vector.add(cur_door_pos,v)
|
||||
other_side_of_door = vector.add(cur_door_pos,-v)
|
||||
table.insert(prospective_wp, cur_door_pos)
|
||||
|
||||
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
|
||||
mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door))
|
||||
|
||||
prospective_wp = minetest.find_path(p, pos_closest_to_door, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||
|
||||
if prospective_wp then
|
||||
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
|
||||
if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then
|
||||
append_paths (prospective_wp, wp_otherside_door_to_target)
|
||||
mcl_log("We have a path from outside door to target")
|
||||
return generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos)
|
||||
else
|
||||
mcl_log("No t, just add other side of door")
|
||||
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
|
||||
mcl_log("We cannot path from outside door to target")
|
||||
end
|
||||
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
|
||||
else
|
||||
mcl_log("Cannot path to this air block next to door.")
|
||||
end
|
||||
end
|
||||
else
|
||||
mcl_log("No door found")
|
||||
end
|
||||
|
||||
if wp and not enriched_path then
|
||||
mcl_log("Wp but not enriched")
|
||||
enriched_path = generate_enriched_path(wp)
|
||||
end
|
||||
return enriched_path
|
||||
end
|
||||
|
||||
-- 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)
|
||||
if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end
|
||||
|
@ -209,8 +180,19 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||
|
||||
self.order = nil
|
||||
|
||||
local p = self.object:get_pos()
|
||||
local t = vector.offset(target,0,1,0)
|
||||
-- maybe feet are buried in solid?
|
||||
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
|
||||
local wp = minetest.find_path(p, t, PATHFINDING_SEARCH_DISTANCE, 1, 4)
|
||||
|
@ -218,11 +200,15 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||
if not wp then
|
||||
mcl_log("### No direct path. Path through door closest to target.")
|
||||
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)
|
||||
|
||||
if not wp then
|
||||
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 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)
|
||||
|
||||
-- Path through 2 doors
|
||||
|
@ -236,7 +222,7 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||
|
||||
local pos_after_door_entry = path_through_closest_door[#path_through_closest_door]
|
||||
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))
|
||||
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
|
||||
|
@ -268,26 +254,92 @@ function mob_class:gopath(target, callback_arrived, prioritised)
|
|||
-- If cannot path, don't immediately try again
|
||||
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
|
||||
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)
|
||||
self._target = t
|
||||
self.callback_arrived = callback_arrived
|
||||
local current_location = table.remove(wp,1)
|
||||
if current_location and current_location["pos"] then
|
||||
mcl_log("Removing first co-ord? " .. tostring(current_location["pos"]))
|
||||
else
|
||||
mcl_log("Nil pos")
|
||||
self.current_target = table.remove(wp,1)
|
||||
while self.current_target and self.current_target.pos and vector.distance(p, self.current_target.pos) < 0.5 do
|
||||
--mcl_log("Skipping close initial waypoint")
|
||||
self.current_target = table.remove(wp,1)
|
||||
end
|
||||
if self.current_target and self.current_target.pos then
|
||||
self:turn_in_direction(self.current_target.pos.x - p.x, self.current_target.pos.z - p.z, 2)
|
||||
self.waypoints = wp
|
||||
self.state = PATHFINDING
|
||||
return true
|
||||
end
|
||||
self.current_target = current_location
|
||||
self.waypoints = wp
|
||||
self.state = PATHFINDING
|
||||
return true
|
||||
else
|
||||
self.state = "walk"
|
||||
self.waypoints = nil
|
||||
self.current_target = nil
|
||||
-- minetest.log("no path found")
|
||||
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
|
||||
|
||||
function mob_class:interact_with_door(action, target)
|
||||
|
@ -300,19 +352,27 @@ function mob_class:interact_with_door(action, target)
|
|||
|
||||
local n = minetest.get_node(target)
|
||||
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
|
||||
local meta = minetest.get_meta(target)
|
||||
local closed = meta:get_int("is_open") == 0
|
||||
if closed and action == "open" and def.on_rightclick then
|
||||
mcl_log("Open door")
|
||||
def.on_rightclick(target,n,self)
|
||||
elseif not closed and action == "close" and def.on_rightclick then
|
||||
mcl_log("Close door")
|
||||
def.on_rightclick(target,n,self)
|
||||
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
|
||||
mcl_log("Not door")
|
||||
end
|
||||
|
@ -333,6 +393,7 @@ function mob_class:do_pathfind_action(action)
|
|||
end
|
||||
if type and type == "door" then
|
||||
mcl_log("Type is door")
|
||||
self.object:set_velocity(vector.zero())
|
||||
self:interact_with_door(action_val, target)
|
||||
end
|
||||
end
|
||||
|
@ -343,8 +404,7 @@ function mob_class:check_gowp(dtime)
|
|||
|
||||
-- no destination
|
||||
if not p or not self._target then
|
||||
mcl_log("p: ".. tostring(p))
|
||||
mcl_log("self._target: ".. tostring(self._target))
|
||||
mcl_log("p: ".. tostring(p)..", self._target: ".. tostring(self._target))
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -358,8 +418,8 @@ function mob_class:check_gowp(dtime)
|
|||
self.current_target = nil
|
||||
self.state = "stand"
|
||||
self.order = "stand"
|
||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
self.object:set_velocity(vector.zero())
|
||||
self.object:set_acceleration(vector.zero())
|
||||
if self.callback_arrived then return self.callback_arrived(self) end
|
||||
return true
|
||||
elseif not self.current_target then
|
||||
|
@ -368,41 +428,67 @@ function mob_class:check_gowp(dtime)
|
|||
|
||||
-- More pathing to be done
|
||||
local distance_to_current_target = 50
|
||||
if self.current_target and self.current_target["pos"] then
|
||||
distance_to_current_target = vector.distance(p,self.current_target["pos"])
|
||||
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
|
||||
distance_to_current_target = (dx*dx+dy*dy*0.5+dz*dz)^0.5 -- reduced weight on y
|
||||
--distance_to_current_target = vector.distance(p,self.current_target.pos)
|
||||
end
|
||||
-- also check next target, maybe we were too fast
|
||||
local next_target = #self.waypoints > 1 and self.waypoints[1]
|
||||
if not self.current_target["action"] and next_target and next_target.pos and distance_to_current_target < 1.5 then
|
||||
local dx, dy, dz = next_target.pos.x-p.x, next_target.pos.y-p.y, next_target.pos.z-p.z
|
||||
local distance_to_next_target = (dx*dx+dy*dy*0.5+dz*dz)^0.5 -- reduced weight on y
|
||||
if distance_to_next_target < distance_to_current_target then
|
||||
mcl_log("Skipped one waypoint.")
|
||||
self.current_target = table.remove(self.waypoints, 1) -- pop waypoint already
|
||||
distance_to_current_target = distance_to_next_target
|
||||
end
|
||||
end
|
||||
-- debugging tool
|
||||
if visualize and self.current_target and self.current_target.pos then
|
||||
core.add_particle({pos = self.current_target.pos, expirationtime=.1, size=3, velocity=vector.new(0,-0.2,0), texture="mcl_particles_flame.png"})
|
||||
end
|
||||
|
||||
-- 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.9 and 1.0 is also good. Stick with unless door open or closing issues
|
||||
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < 0.9 ) then
|
||||
local threshold = self.current_target["action"] and 0.7 or 0.9
|
||||
if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target.pos or distance_to_current_target < threshold ) then
|
||||
-- We have waypoints, and are at current_target or have no current target. We need a new current_target.
|
||||
self:do_pathfind_action (self.current_target["action"])
|
||||
|
||||
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:go_to_pos(self.current_target["pos"])
|
||||
-- use smoothing -- TODO: check for blockers before cutting corners?
|
||||
if #self.waypoints > 0 and not self.current_target["action"] then
|
||||
local curwp, nextwp = self.current_target.pos, self.waypoints[1].pos
|
||||
self:go_to_pos(vector.new(curwp.x*0.7+nextwp.x*0.3,curwp.y,curwp.z*0.7+nextwp.z*0.3), hurry)
|
||||
return
|
||||
end
|
||||
self:go_to_pos(self.current_target.pos, hurry)
|
||||
--if self.current_target["action"] then self:set_velocity(self.walk_velocity * 0.5) end
|
||||
return
|
||||
elseif self.current_target and self.current_target["pos"] then
|
||||
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.
|
||||
|
||||
self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1
|
||||
local failed_attempts = self.current_target["failed_attempts"]
|
||||
if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then
|
||||
mcl_log("Failed to reach position (" .. minetest.pos_to_string(self.current_target["pos"]) .. ") too many times. Abandon route. Times tried: " .. failed_attempts)
|
||||
mcl_log("Failed to reach position " .. minetest.pos_to_string(self.current_target.pos) .. " too many times. At: "..minetest.pos_to_string(p).." Abandon route. Times tried: " .. failed_attempts .. " current distance "..distance_to_current_target)
|
||||
self.state = "stand"
|
||||
self.current_target = nil
|
||||
self.waypoints = nil
|
||||
self._target = nil
|
||||
self._pf_last_failed = os.time()
|
||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
self.object:set_velocity(vector.zero())
|
||||
self.object:set_acceleration(vector.zero())
|
||||
return
|
||||
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"])
|
||||
-- Do i just delete current_target, and return so we can find final path.
|
||||
else
|
||||
|
@ -436,6 +522,7 @@ function mob_class:check_gowp(dtime)
|
|||
|
||||
-- I don't think we need the following anymore, but test first.
|
||||
-- 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
|
||||
local updated_p = self.object:get_pos()
|
||||
local distance_to_cur_targ = vector.distance(updated_p,self.current_target["pos"])
|
||||
|
@ -444,7 +531,7 @@ function mob_class:check_gowp(dtime)
|
|||
mcl_log("Current p: ".. minetest.pos_to_string(updated_p))
|
||||
|
||||
-- 1.6 is good. is 1.9 better? It could fail less, but will it path to door when it isn't after door
|
||||
if distance_to_cur_targ > 1.9 then
|
||||
if distance_to_cur_targ > 1.6 then
|
||||
mcl_log("not close to current target: ".. minetest.pos_to_string(self.current_target["pos"]))
|
||||
self:go_to_pos(self._current_target)
|
||||
else
|
||||
|
@ -454,4 +541,5 @@ function mob_class:check_gowp(dtime)
|
|||
end
|
||||
return
|
||||
end
|
||||
--]]--
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -12,9 +12,8 @@ local axolotl = {
|
|||
xp_max = 7,
|
||||
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = -1,
|
||||
head_eye_height = -0.5,
|
||||
horizontal_head_height = 0,
|
||||
head_eye_height = 0.5,
|
||||
head_bone_position = vector.new( 0, -1, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
head_yaw="z",
|
||||
|
||||
|
|
|
@ -26,14 +26,14 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
|
|||
xp_min = 10,
|
||||
xp_max = 10,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.79, 0.3},
|
||||
rotate = -180,
|
||||
rotate = 180,
|
||||
head_yaw_offset = 180,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_blaze.b3d",
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 4,
|
||||
head_eye_height = 3.5,
|
||||
head_eye_height = 1.4,
|
||||
head_bone_position = vector.new( 0, 4, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
head_yaw_offset = 180,
|
||||
head_pitch_multiplier=-1,
|
||||
textures = {
|
||||
{"mobs_mc_blaze.png"},
|
||||
|
@ -83,7 +83,7 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
|
|||
shoot_offset = 1.0,
|
||||
passive = false,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
fear_height = 0,
|
||||
|
|
|
@ -21,9 +21,8 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
|||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.69, 0.2},
|
||||
floats = 1,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 4,
|
||||
head_eye_height = 1.5,
|
||||
horizontal_head_height = -.3,
|
||||
head_eye_height = 0.5,
|
||||
head_bone_position = vector.new(0, 4, -.3), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
head_yaw="z",
|
||||
visual_size = {x=1,y=1},
|
||||
|
|
|
@ -82,7 +82,7 @@ local cod = {
|
|||
do_custom = function(self)
|
||||
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
|
||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
||||
if self.standing_in.groups.water then
|
||||
if self.object:get_velocity().y < 5 then
|
||||
self.object:add_velocity({ x = 0 , y = math.random()*.014-.007, z = 0 })
|
||||
end
|
||||
|
|
|
@ -22,9 +22,8 @@ local cow_def = {
|
|||
"blank.png",
|
||||
}, },
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 10,
|
||||
head_eye_height = 1.1,
|
||||
horizontal_head_height=-1.8,
|
||||
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 2,
|
||||
head_yaw="z",
|
||||
makes_footstep_sound = true,
|
||||
|
|
|
@ -84,7 +84,7 @@ local dolphin = {
|
|||
--[[ this is supposed to make them jump out the water but doesn't appear to work very well
|
||||
do_custom = function(self,dtime)
|
||||
self.object:set_bone_position("body", vector.new(0,1,0), vector.new(degrees(dir_to_pitch(self.object:get_velocity())) * -1 + 90,0,0))
|
||||
if minetest.get_item_group(self.standing_in, "water") ~= 0 then
|
||||
if self.standing_in.groups.water then
|
||||
if self.object:get_velocity().y < 5 then
|
||||
self.object:add_velocity({ x = 0 , y = math.random()*.014-.007, z = 0 })
|
||||
end
|
||||
|
|
|
@ -77,7 +77,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
|||
damage = 10,
|
||||
knock_back = false,
|
||||
jump = true,
|
||||
jump_height = 14,
|
||||
jump_height = 10,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
dogshoot_switch = 1,
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
-- Model: pixelzone https://codeberg.org/mineclonia/mineclonia/issues/2118
|
||||
-- Texture: Pixel Perfection CC-BY-SA https://github.com/NovaWostra/Pixel-Perfection-Chorus-Eddit/issues/8
|
||||
-- Sounds:
|
||||
-- mobs_mc_fox_bark.1.ogg derived from CC-0 https://freesound.org/people/craigsays/sounds/537587/
|
||||
-- mobs_mc_fox_bark.2.ogg derived from CC-0 https://freesound.org/people/craigsays/sounds/537587/
|
||||
-- mobs_mc_fox_hurt.1.ogg derived from CC-0 https://freesound.org/people/Soundburst/sounds/634005/
|
||||
-- mobs_mc_fox_growl.1.ogg derived from CC-0 https://freesound.org/people/tilano408/sounds/445658/
|
||||
|
||||
-- TODO: carry one item, spawn with item
|
||||
-- TODO: add sleeping behavior
|
||||
-- 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" },
|
||||
},
|
||||
makes_footstep_sound = true,
|
||||
head_swivel = "Bone.001",
|
||||
head_yaw = "z",
|
||||
head_eye_height = 0.5,
|
||||
head_bone_position = vector.new( 0, 0.5, 0 ), -- for minetest <5.8
|
||||
curiosity = 5,
|
||||
sounds = {
|
||||
attack = "mobs_mc_fox_bark",
|
||||
war_cry = "mobs_mc_fox_growl",
|
||||
damage = "mobs_mc_fox_hurt",
|
||||
death = {name = "mobs_mc_wolf_death", gain=0.6},
|
||||
eat = "mobs_mc_animal_eat_generic",
|
||||
distance = 12,
|
||||
},
|
||||
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
|
||||
on_rightclick = function(self, clicker)
|
||||
if self:feed_tame(clicker, 1, true, true) then return end
|
||||
if mcl_mobs:protect(self, clicker) then return end
|
||||
end,
|
||||
on_breed = function(parent1, parent2)
|
||||
local p = math.random(1,2) == 1 and parent1 or parent2
|
||||
local pos = parent1.object:get_pos()
|
||||
if not pos then return false end
|
||||
local child = mcl_mobs.spawn_child(pos, p.name)
|
||||
if not child then return false end
|
||||
local ent_c = child:get_luaentity()
|
||||
ent_c.tamed = true
|
||||
ent_c.owner = parent1.owner
|
||||
return false
|
||||
end,
|
||||
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 = 4, -- TODO: when attacking, allow to jump higher
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
-- note: breeding code uses the convention that fox and fox_snow can breed
|
||||
local fox_snow = table.copy(fox)
|
||||
fox_snow.textures = { { "mobs_mc_snow_fox.png", "mobs_mc_snow_fox_sleep.png" } },
|
||||
|
||||
mcl_mobs.register_mob("mobs_mc:fox", fox)
|
||||
mcl_mobs.register_mob("mobs_mc:fox_snow", fox_snow)
|
||||
-- Spawn
|
||||
mcl_mobs:spawn_specific(
|
||||
"mobs_mc:fox",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"Taiga",
|
||||
"Taiga_beach",
|
||||
"MegaTaiga",
|
||||
"MegaSpruceTaiga",
|
||||
},
|
||||
0,
|
||||
minetest.LIGHT_MAX+1,
|
||||
30,
|
||||
80,
|
||||
7,
|
||||
mobs_mc.water_level+3,
|
||||
mcl_vars.mg_overworld_max)
|
||||
-- Spawn
|
||||
mcl_mobs:spawn_specific(
|
||||
"mobs_mc:fox_snow",
|
||||
"overworld",
|
||||
"ground",
|
||||
{
|
||||
"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)
|
|
@ -52,6 +52,7 @@ mcl_mobs.register_mob("mobs_mc:ghast", {
|
|||
fall_damage = 0,
|
||||
view_range = 64,
|
||||
attack_type = "dogshoot",
|
||||
see_through_opaque = false,
|
||||
arrow = "mobs_mc:fireball",
|
||||
shoot_interval = 5,
|
||||
shoot_offset = -0.5,
|
||||
|
@ -59,7 +60,7 @@ mcl_mobs.register_mob("mobs_mc:ghast", {
|
|||
dogshoot_count_max =1,
|
||||
passive = false,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
floats=1,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
|
|
|
@ -158,7 +158,7 @@ local horse = {
|
|||
floats = 1,
|
||||
makes_footstep_sound = true,
|
||||
jump = true,
|
||||
jump_height = 5.75, -- can clear 2.5 blocks
|
||||
jump_height = 2.5, -- can clear 2.5 blocks
|
||||
drops = {
|
||||
{name = "mcl_mobitems:leather",
|
||||
chance = 1,
|
||||
|
@ -560,7 +560,7 @@ donkey.collisionbox = {
|
|||
horse.collisionbox[6] * d,
|
||||
}
|
||||
donkey.jump = true
|
||||
donkey.jump_height = 3.75 -- can clear 1 block height
|
||||
donkey.jump_height = 1 -- can clear 1 block height
|
||||
|
||||
|
||||
mcl_mobs.register_mob("mobs_mc:donkey", donkey)
|
||||
|
|
|
@ -149,6 +149,7 @@ dofile(path .. "/salmon.lua")
|
|||
dofile(path .. "/tropical_fish.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")
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
|
|||
visual = "mesh",
|
||||
mesh = "mobs_mc_iron_golem.b3d",
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 3.38,
|
||||
head_eye_height = 2.5,
|
||||
head_bone_position = vector.new( 0, 3.38, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
textures = {
|
||||
{"mobs_mc_iron_golem.png"},
|
||||
|
|
|
@ -60,11 +60,10 @@ mcl_mobs.register_mob("mobs_mc:llama", {
|
|||
spawn_in_group = 4, -- was 6 nerfed until we can cap them properly locally. this is a group size, not a per spawn attempt
|
||||
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 11,
|
||||
head_eye_height = 3,
|
||||
horizontal_head_height=0,
|
||||
curiosity = 60,
|
||||
head_eye_height = 1.5,
|
||||
head_yaw = "z",
|
||||
head_bone_position = vector.new( 0, 11, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 60,
|
||||
|
||||
hp_min = 15,
|
||||
hp_max = 30,
|
||||
|
|
Binary file not shown.
|
@ -37,9 +37,8 @@ local ocelot = {
|
|||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 6.2,
|
||||
head_eye_height = 0.4,
|
||||
horizontal_head_height=-0,
|
||||
head_bone_position = vector.new( 0, 6.2, 0 ), -- for minetest <= 5.8
|
||||
head_yaw="z",
|
||||
curiosity = 4,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.69, 0.3},
|
||||
|
|
|
@ -136,8 +136,7 @@ mcl_mobs.register_mob("mobs_mc:parrot", {
|
|||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 1.1,
|
||||
horizontal_head_height=0,
|
||||
head_bone_position = vector.new( 0, 1.1, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.89, 0.25},
|
||||
visual = "mesh",
|
||||
|
|
|
@ -20,9 +20,8 @@ mcl_mobs.register_mob("mobs_mc:pig", {
|
|||
"blank.png", -- saddle
|
||||
}},
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 7.5,
|
||||
head_eye_height = 0.8,
|
||||
horizontal_head_height=-1,
|
||||
head_eye_height = 0.7,
|
||||
head_bone_position = vector.new( 0, 7.5, -1 ), -- for minetest <= 5.8
|
||||
curiosity = 3,
|
||||
head_yaw="z",
|
||||
makes_footstep_sound = true,
|
||||
|
|
|
@ -252,7 +252,7 @@ local zombified_piglin = {
|
|||
damage = 9,
|
||||
reach = 2,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 2.4,
|
||||
head_bone_position = vector.new( 0, 2.4, 0 ), -- for minetest <= 5.8
|
||||
head_eye_height = 1.4,
|
||||
curiosity = 15,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3}, -- same
|
||||
|
@ -325,6 +325,7 @@ mcl_mobs.register_mob("mobs_mc:zombified_piglin", zombified_piglin)
|
|||
local baby_zombified_piglin = table.copy(zombified_piglin)
|
||||
baby_zombified_piglin.description = S("Baby Zombie Piglin")
|
||||
baby_zombified_piglin.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.94, 0.25}
|
||||
baby_zombified_piglin.head_eye_height = 0.8
|
||||
baby_zombified_piglin.xp_min = 13
|
||||
baby_zombified_piglin.xp_max = 13
|
||||
baby_zombified_piglin.textures = {
|
||||
|
|
|
@ -1,125 +1,125 @@
|
|||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
local function reload(self)
|
||||
if not self.object:get_pos() then return end
|
||||
minetest.sound_play("mcl_bows_crossbow_drawback_1", {object = self.object, max_hear_distance=16}, true)
|
||||
local props = self.object:get_properties()
|
||||
if not props then return end
|
||||
props.textures[2] = "mcl_bows_crossbow_3.png^[resize:16x16"
|
||||
self.object:set_properties(props)
|
||||
end
|
||||
|
||||
local function reset_animation(self, animation)
|
||||
if not self.object:get_pos() or self._current_animation ~= animation then return end
|
||||
self._current_animation = "stand_reload" -- Mobs Redo won't set the animation unless we do this
|
||||
self:set_animation(animation)
|
||||
end
|
||||
|
||||
pillager = {
|
||||
description = S("Pillager"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
hp_min = 24,
|
||||
hp_max = 24,
|
||||
xp_min = 6,
|
||||
xp_max = 6,
|
||||
breath_max = -1,
|
||||
eye_height = 1.5,
|
||||
shoot_interval = 3,
|
||||
shoot_offset = 1.5,
|
||||
armor = {fleshy = 100},
|
||||
can_despawn = false,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.98, 0.3},
|
||||
pathfinding = 1,
|
||||
group_attack = true,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_pillager.b3d",
|
||||
visual_size = {x=2.75, y=2.75},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1.2,
|
||||
run_velocity = 4,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
arrow = "mcl_bows:arrow_entity",
|
||||
attack_type = "dogshoot", -- Alternate punching/shooting
|
||||
attack_npcs = true,
|
||||
reach = 0, -- Punching max distance
|
||||
damage = 0, -- Punching damage
|
||||
dogshoot_switch = 1, -- Start of shooting
|
||||
dogshoot_count_max = 5, -- Max time spent shooting (standing)
|
||||
dogshoot_count2_max = 1, -- Max time spent punching (running)
|
||||
sounds = {
|
||||
random = "mobs_mc_pillager_grunt2",
|
||||
war_cry = "mobs_mc_pillager_grunt1",
|
||||
death = "mobs_mc_pillager_ow2",
|
||||
damage = "mobs_mc_pillager_ow1",
|
||||
distance = 16,
|
||||
},
|
||||
textures = {
|
||||
{
|
||||
"mobs_mc_pillager.png", -- Skin
|
||||
"mcl_bows_crossbow_3.png^[resize:16x16", -- Wielded item
|
||||
}
|
||||
},
|
||||
drops = {
|
||||
{
|
||||
name = "mcl_bows:arrow",
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
looting = "common",
|
||||
},
|
||||
{
|
||||
name = "mcl_bows:crossbow",
|
||||
chance = 100 / 8.5,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
},
|
||||
},
|
||||
animation = {
|
||||
unloaded_walk_start = 1, unloaded_walk_end = 40,
|
||||
unloaded_stand_start = 41, unloaded_stand_end = 60,
|
||||
reload_stand_start = 61, reload_stand_end = 100, reload_stand_speed = 20,
|
||||
stand_start = 101, stand_end = 109, stand_speed = 6,
|
||||
walk_start = 111, walk_end = 150, walk_speed = 30,
|
||||
run_start = 111, run_end = 150, run_speed = 50,
|
||||
reload_run_start = 151, reload_run_end = 190, reload_run_speed = 20,
|
||||
die_start = 191, die_end = 192, die_speed = 15,
|
||||
stand_unloaded_start = 40, stand_unloaded_end = 59,
|
||||
die_loop = false,
|
||||
},
|
||||
shoot_arrow = function(self, pos, dir)
|
||||
minetest.sound_play("mcl_bows_crossbow_shoot", {object = self.object, max_hear_distance=16}, true)
|
||||
local props = self.object:get_properties()
|
||||
props.textures[2] = "mcl_bows_crossbow_0.png^[resize:16x16"
|
||||
self.object:set_properties(props)
|
||||
local old_anim = self._current_animation
|
||||
if old_anim == "run" or old_anim == "walk" then
|
||||
self:set_animation("reload_run")
|
||||
end
|
||||
if old_anim == "stand" then
|
||||
self:set_animation("reload_stand")
|
||||
end
|
||||
self._current_animation = old_anim -- Mobs Redo will imediately reset the animation otherwise
|
||||
minetest.after(1, reload, self)
|
||||
minetest.after(2, reset_animation, self, old_anim)
|
||||
|
||||
-- 2-4 damage per arrow
|
||||
local dmg = math.max(4, math.random(2, 8))
|
||||
mcl_bows_s.shoot_arrow_crossbow("mcl_bows:arrow", pos, dir, self.object:get_yaw(), self.object, nil, dmg)
|
||||
|
||||
-- While we are at it, change the sounds since there is no way to do this in Mobs Redo
|
||||
if self.sounds and self.sounds.random then
|
||||
self.sounds = table.copy(self.sounds)
|
||||
self.sounds.random = "mobs_mc_pillager_grunt" .. math.random(2)
|
||||
end
|
||||
|
||||
-- Randomize reload time
|
||||
self.shoot_interval = math.random(3, 4)
|
||||
end,
|
||||
}
|
||||
|
||||
mcl_mobs.register_mob("mobs_mc:pillager", pillager)
|
||||
mcl_mobs.register_egg("mobs_mc:pillager", S("Pillager"), "#532f36", "#959b9b", 0)
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
local function reload(self)
|
||||
if not self.object:get_pos() then return end
|
||||
minetest.sound_play("mcl_bows_crossbow_drawback_1", {object = self.object, max_hear_distance=16}, true)
|
||||
local props = self.object:get_properties()
|
||||
if not props then return end
|
||||
props.textures[2] = "mcl_bows_crossbow_3.png^[resize:16x16"
|
||||
self.object:set_properties(props)
|
||||
end
|
||||
|
||||
local function reset_animation(self, animation)
|
||||
if not self.object:get_pos() or self._current_animation ~= animation then return end
|
||||
self._current_animation = "stand_reload" -- Mobs Redo won't set the animation unless we do this
|
||||
self:set_animation(animation)
|
||||
end
|
||||
|
||||
pillager = {
|
||||
description = S("Pillager"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
hp_min = 24,
|
||||
hp_max = 24,
|
||||
xp_min = 6,
|
||||
xp_max = 6,
|
||||
breath_max = -1,
|
||||
eye_height = 1.5,
|
||||
shoot_interval = 3,
|
||||
shoot_offset = 1.5,
|
||||
armor = {fleshy = 100},
|
||||
can_despawn = false,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.98, 0.3},
|
||||
pathfinding = 1,
|
||||
group_attack = true,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_pillager.b3d",
|
||||
visual_size = {x=2.75, y=2.75},
|
||||
makes_footstep_sound = true,
|
||||
walk_velocity = 1.2,
|
||||
run_velocity = 4,
|
||||
view_range = 16,
|
||||
fear_height = 4,
|
||||
arrow = "mcl_bows:arrow_entity",
|
||||
attack_type = "dogshoot", -- Alternate punching/shooting
|
||||
attack_npcs = true,
|
||||
reach = 2, -- Punching max distance
|
||||
damage = 2, -- Punching damage
|
||||
dogshoot_switch = 1, -- Start of shooting
|
||||
dogshoot_count_max = 4, -- Max time spent shooting (standing)
|
||||
dogshoot_count2_max = 1, -- Max time spent punching (running)
|
||||
sounds = {
|
||||
random = "mobs_mc_pillager_grunt2",
|
||||
war_cry = "mobs_mc_pillager_grunt1",
|
||||
death = "mobs_mc_pillager_ow2",
|
||||
damage = "mobs_mc_pillager_ow1",
|
||||
distance = 16,
|
||||
},
|
||||
textures = {
|
||||
{
|
||||
"mobs_mc_pillager.png", -- Skin
|
||||
"mcl_bows_crossbow_3.png^[resize:16x16", -- Wielded item
|
||||
}
|
||||
},
|
||||
drops = {
|
||||
{
|
||||
name = "mcl_bows:arrow",
|
||||
chance = 1,
|
||||
min = 0,
|
||||
max = 2,
|
||||
looting = "common",
|
||||
},
|
||||
{
|
||||
name = "mcl_bows:crossbow",
|
||||
chance = 100 / 8.5,
|
||||
min = 1,
|
||||
max = 1,
|
||||
looting = "rare",
|
||||
},
|
||||
},
|
||||
animation = {
|
||||
unloaded_walk_start = 1, unloaded_walk_end = 40,
|
||||
unloaded_stand_start = 41, unloaded_stand_end = 60,
|
||||
reload_stand_start = 61, reload_stand_end = 100, reload_stand_speed = 20,
|
||||
stand_start = 101, stand_end = 109, stand_speed = 6,
|
||||
walk_start = 111, walk_end = 150, walk_speed = 30,
|
||||
run_start = 111, run_end = 150, run_speed = 50,
|
||||
reload_run_start = 151, reload_run_end = 190, reload_run_speed = 20,
|
||||
die_start = 191, die_end = 192, die_speed = 15,
|
||||
stand_unloaded_start = 40, stand_unloaded_end = 59,
|
||||
die_loop = false,
|
||||
},
|
||||
shoot_arrow = function(self, pos, dir)
|
||||
minetest.sound_play("mcl_bows_crossbow_shoot", {object = self.object, max_hear_distance=16}, true)
|
||||
local props = self.object:get_properties()
|
||||
props.textures[2] = "mcl_bows_crossbow_0.png^[resize:16x16"
|
||||
self.object:set_properties(props)
|
||||
local old_anim = self._current_animation
|
||||
if old_anim == "run" or old_anim == "walk" then
|
||||
self:set_animation("reload_run")
|
||||
end
|
||||
if old_anim == "stand" then
|
||||
self:set_animation("reload_stand")
|
||||
end
|
||||
self._current_animation = old_anim -- Mobs Redo will imediately reset the animation otherwise
|
||||
minetest.after(1, reload, self)
|
||||
minetest.after(2, reset_animation, self, old_anim)
|
||||
|
||||
-- 2-4 damage per arrow
|
||||
local dmg = math.max(4, math.random(2, 8))
|
||||
mcl_bows_s.shoot_arrow_crossbow("mcl_bows:arrow", pos, dir, self.object:get_yaw(), self.object, nil, dmg)
|
||||
|
||||
-- While we are at it, change the sounds since there is no way to do this in Mobs Redo
|
||||
if self.sounds and self.sounds.random then
|
||||
self.sounds = table.copy(self.sounds)
|
||||
self.sounds.random = "mobs_mc_pillager_grunt" .. math.random(2)
|
||||
end
|
||||
|
||||
-- Randomize reload time
|
||||
self.shoot_interval = math.random(3, 4)
|
||||
end,
|
||||
}
|
||||
|
||||
mcl_mobs.register_mob("mobs_mc:pillager", pillager)
|
||||
mcl_mobs.register_egg("mobs_mc:pillager", S("Pillager"), "#532f36", "#959b9b", 0)
|
||||
mcl_mobs:non_spawn_specific("mobs_mc:pillager","overworld",0,7)
|
||||
|
|
|
@ -25,9 +25,8 @@ mcl_mobs.register_mob("mobs_mc:polar_bear", {
|
|||
{"mobs_mc_polarbear.png"},
|
||||
},
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 2.6,
|
||||
head_eye_height = 1,
|
||||
horizontal_head_height = 0,
|
||||
head_bone_position = vector.new( 0, 2.6, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 20,
|
||||
head_yaw="z",
|
||||
visual_size = {x=3.0, y=3.0},
|
||||
|
|
|
@ -16,20 +16,19 @@ local rabbit = {
|
|||
xp_max = 3,
|
||||
collisionbox = {-0.2, -0.01, -0.2, 0.2, 0.49, 0.2},
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 2,
|
||||
head_eye_height = 0.5,
|
||||
horizontal_head_height = -.3,
|
||||
head_eye_height = 0.35,
|
||||
head_bone_position = vector.new( 0, 2, -.3 ), -- for minetest <= 5.8
|
||||
curiosity = 20,
|
||||
head_yaw="z",
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_rabbit.b3d",
|
||||
textures = {
|
||||
{"mobs_mc_rabbit_brown.png"},
|
||||
{"mobs_mc_rabbit_gold.png"},
|
||||
{"mobs_mc_rabbit_white.png"},
|
||||
{"mobs_mc_rabbit_white_splotched.png"},
|
||||
{"mobs_mc_rabbit_salt.png"},
|
||||
{"mobs_mc_rabbit_black.png"},
|
||||
{"mobs_mc_rabbit_brown.png"},
|
||||
{"mobs_mc_rabbit_gold.png"},
|
||||
{"mobs_mc_rabbit_white.png"},
|
||||
{"mobs_mc_rabbit_white_splotched.png"},
|
||||
{"mobs_mc_rabbit_salt.png"},
|
||||
{"mobs_mc_rabbit_black.png"},
|
||||
},
|
||||
sounds = {
|
||||
random = "mobs_mc_rabbit_random",
|
||||
|
|
|
@ -65,9 +65,8 @@ mcl_mobs.register_mob("mobs_mc:sheep", {
|
|||
xp_max = 3,
|
||||
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.29, 0.45},
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 3.3,
|
||||
head_eye_height = 1.1,
|
||||
horizontal_head_height=-.7,
|
||||
head_eye_height = 1.0,
|
||||
head_bone_position = vector.new( 0, 3.3, -.7 ), -- for minetest <= 5.8
|
||||
curiosity = 6,
|
||||
head_yaw="z",
|
||||
visual = "mesh",
|
||||
|
|
|
@ -83,7 +83,6 @@ mcl_mobs.register_mob("mobs_mc:shulker", {
|
|||
local pos = self.object:get_pos()
|
||||
if math.floor(self.object:get_yaw()) ~=0 then
|
||||
self.object:set_yaw(0)
|
||||
mcl_mobs:yaw(self, 0, 0, dtime)
|
||||
end
|
||||
if self.state == "attack" then
|
||||
self:set_animation("run")
|
||||
|
|
|
@ -26,7 +26,8 @@ local skeleton = {
|
|||
pathfinding = 1,
|
||||
group_attack = true,
|
||||
head_swivel = "Head_Control",
|
||||
bone_eye_height = 2.38,
|
||||
head_eye_height = 1.5,
|
||||
head_bone_position = vector.new( 0, 2.38, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 6,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_skeleton.b3d",
|
||||
|
|
|
@ -25,7 +25,8 @@ mcl_mobs.register_mob("mobs_mc:witherskeleton", {
|
|||
visual = "mesh",
|
||||
mesh = "mobs_mc_witherskeleton.b3d",
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 2.38,
|
||||
head_eye_height = 1.5,
|
||||
head_bone_position = vector.new( 0, 2.38, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 60,
|
||||
textures = {
|
||||
{
|
||||
|
|
|
@ -134,7 +134,7 @@ local slime_big = {
|
|||
walk_velocity = 1.9,
|
||||
run_velocity = 1.9,
|
||||
walk_chance = 0,
|
||||
jump_height = 5.2,
|
||||
jump_height = 1,
|
||||
fear_height = 0,
|
||||
spawn_small_alternative = "mobs_mc:slime_small",
|
||||
on_die = spawn_children_on_die("mobs_mc:slime_small", 1.0, 1.5),
|
||||
|
@ -156,7 +156,6 @@ slime_small.damage = 3
|
|||
slime_small.reach = 2.25
|
||||
slime_small.walk_velocity = 1.8
|
||||
slime_small.run_velocity = 1.8
|
||||
slime_small.jump_height = 4.3
|
||||
slime_small.spawn_small_alternative = "mobs_mc:slime_tiny"
|
||||
slime_small.on_die = spawn_children_on_die("mobs_mc:slime_tiny", 0.6, 1.0)
|
||||
mcl_mobs.register_mob("mobs_mc:slime_small", slime_small)
|
||||
|
@ -181,7 +180,6 @@ slime_tiny.drops = {
|
|||
}
|
||||
slime_tiny.walk_velocity = 1.7
|
||||
slime_tiny.run_velocity = 1.7
|
||||
slime_tiny.jump_height = 3
|
||||
slime_tiny.spawn_small_alternative = nil
|
||||
slime_tiny.on_die = nil
|
||||
|
||||
|
@ -363,7 +361,7 @@ local magma_cube_big = {
|
|||
attack_type = "dogfight",
|
||||
passive = false,
|
||||
jump = true,
|
||||
jump_height = 8,
|
||||
jump_height = 4,
|
||||
walk_chance = 0,
|
||||
fear_height = 0,
|
||||
spawn_small_alternative = "mobs_mc:magma_cube_small",
|
||||
|
@ -386,7 +384,7 @@ magma_cube_small.damage = 3
|
|||
magma_cube_small.reach = 2.1
|
||||
magma_cube_small.walk_velocity = .8
|
||||
magma_cube_small.run_velocity = 2.0
|
||||
magma_cube_small.jump_height = 6
|
||||
magma_cube_small.jump_height = 2
|
||||
magma_cube_small.damage = 4
|
||||
magma_cube_small.reach = 2.75
|
||||
magma_cube_small.armor = 66
|
||||
|
@ -407,7 +405,7 @@ magma_cube_tiny.collisionbox = {-0.2505, -0.01, -0.2505, 0.2505, 0.50, 0.2505, r
|
|||
magma_cube_tiny.visual_size = {x=3.125, y=3.125}
|
||||
magma_cube_tiny.walk_velocity = 1.02
|
||||
magma_cube_tiny.run_velocity = 1.02
|
||||
magma_cube_tiny.jump_height = 4
|
||||
magma_cube_tiny.jump_height = 1
|
||||
magma_cube_tiny.damage = 3
|
||||
magma_cube_tiny.reach = 2
|
||||
magma_cube_tiny.armor = 50
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -63,7 +63,8 @@ local spider = {
|
|||
end
|
||||
end,
|
||||
head_swivel = "Head_Control",
|
||||
bone_eye_height = 1,
|
||||
head_eye_height = 0.6,
|
||||
head_bone_position = vector.new( 0, 1, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
head_yaw="z",
|
||||
collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7},
|
||||
|
@ -86,7 +87,7 @@ local spider = {
|
|||
walk_velocity = 1.3,
|
||||
run_velocity = 2.4,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
view_range = 16,
|
||||
floats = 1,
|
||||
drops = {
|
||||
|
|
|
@ -2,17 +2,36 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
--###################
|
||||
--################### STALKER
|
||||
--###################
|
||||
-- foliage and grass palettes, loaded from mcl_maps
|
||||
local palettes = {}
|
||||
|
||||
local function load_json_file(name)
|
||||
local file = assert(io.open(name, "r"))
|
||||
local data = minetest.parse_json(file:read("*all"))
|
||||
file:close()
|
||||
return data
|
||||
end
|
||||
local mapmodpath = minetest.get_modpath("mcl_maps")
|
||||
if mapmodpath then
|
||||
for k,v in pairs(load_json_file(mapmodpath .. "/palettes_grass.json")) do
|
||||
palettes[k] = v
|
||||
end
|
||||
for k,v in pairs(load_json_file(mapmodpath .. "/palettes_foliage.json")) do
|
||||
palettes[k] = v
|
||||
end
|
||||
for k,v in pairs(load_json_file(mapmodpath .. "/palettes_water.json")) do
|
||||
palettes[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_texture(self)
|
||||
local on_name = self.standing_on
|
||||
local function get_texture(self, prev)
|
||||
local standing_on = minetest.registered_nodes[self.standing_on]
|
||||
-- TODO: we do not have access to param2 here (color palette index) yet
|
||||
local texture
|
||||
local texture_suff = ""
|
||||
if on_name and on_name ~= "air" then
|
||||
local tiles = minetest.registered_nodes[on_name].tiles
|
||||
if standing_on and (standing_on.walkable or standing_on.groups.liquid) then
|
||||
local tiles = standing_on.tiles
|
||||
if tiles then
|
||||
local tile = tiles[1]
|
||||
local color
|
||||
|
@ -25,22 +44,49 @@ local function get_texture(self)
|
|||
texture = tile
|
||||
end
|
||||
if not color then
|
||||
color = minetest.colorspec_to_colorstring(minetest.registered_nodes[on_name].color)
|
||||
color = minetest.colorspec_to_colorstring(standing_on.color)
|
||||
end
|
||||
-- handle param2
|
||||
if standing_on.palette and self.standing_on_node then
|
||||
local param2
|
||||
if standing_on.paramtype2 == "color" then
|
||||
param2 = self.standing_on_node.param2
|
||||
elseif standing_on.paramtype2 == "colorfacedir" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 8)
|
||||
elseif standing_on.paramtype2 == "colorwallmounted" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 32)
|
||||
elseif standing_on.paramtype2 == "color4dir" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 64)
|
||||
elseif standing_on.paramtype2 == "colordegrotate" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 8)
|
||||
end
|
||||
local palette = palettes[standing_on.palette]
|
||||
local oldcol = color
|
||||
if param2 and palette then
|
||||
local c = palette[param2 + 1]
|
||||
if c then color = minetest.rgba(c[1], c[2], c[3], c[4]) end
|
||||
end
|
||||
end
|
||||
if color then
|
||||
texture_suff = "^[multiply:" .. color .. "^[hsl:0:0:20"
|
||||
texture_suff = "^[multiply:" .. color .. "^[contrast:20:10" --"^[hsl:0:0:20"
|
||||
end
|
||||
end
|
||||
end
|
||||
if not texture or texture == "" then
|
||||
-- try to keep last texture when, e.g., falling
|
||||
if prev and (not (not self.attack)) == (string.find(prev, "vl_mobs_stalker_overlay_angry.png") ~= nil) then
|
||||
return prev
|
||||
end
|
||||
texture = "vl_stalker_default.png"
|
||||
end
|
||||
texture = texture:gsub("([\\^:\\[])","\\%1") -- escape texture modifiers
|
||||
texture = "([combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture ..")".. texture_suff
|
||||
if self.attack then
|
||||
texture = texture .. ")^vl_mobs_stalker_overlay_angry.png"
|
||||
if texture_suff then texture = texture .. texture_suff end
|
||||
else
|
||||
texture = texture .. ")^vl_mobs_stalker_overlay.png"
|
||||
texture = texture:gsub("([\\^:\\[])", "\\%1") -- escape texture modifiers
|
||||
texture = "(vl_stalker_default.png^[combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture .. ")" .. texture_suff .. ")"
|
||||
end
|
||||
if self.attack then
|
||||
texture = texture .. "^vl_mobs_stalker_overlay_angry.png"
|
||||
else
|
||||
texture = texture .. "^vl_mobs_stalker_overlay.png"
|
||||
end
|
||||
return texture
|
||||
end
|
||||
|
@ -69,8 +115,8 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
|
|||
visual = "mesh",
|
||||
mesh = "vl_stalker.b3d",
|
||||
-- head_swivel = "Head_Control",
|
||||
bone_eye_height = 2.35,
|
||||
head_eye_height = 1.8;
|
||||
head_eye_height = 1.2;
|
||||
head_bone_position = vector.new( 0, 2.35, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 2,
|
||||
textures = {
|
||||
{get_texture({}),
|
||||
|
@ -98,6 +144,7 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
|
|||
explosion_damage_radius = 3.5,
|
||||
explosiontimer_reset_radius = 3,
|
||||
reach = 3,
|
||||
see_through_opaque = false,
|
||||
explosion_timer = 1.5,
|
||||
allow_fuse_reset = true,
|
||||
stop_to_explode = true,
|
||||
|
@ -132,7 +179,7 @@ mcl_mobs.register_mob("mobs_mc:stalker", {
|
|||
self:boom(mcl_util.get_object_center(self.object), self.explosion_strength)
|
||||
end
|
||||
end
|
||||
local new_texture = get_texture(self)
|
||||
local new_texture = get_texture(self, self._stalker_texture)
|
||||
if self._stalker_texture ~= new_texture then
|
||||
self.object:set_properties({textures={new_texture, "mobs_mc_empty.png"}})
|
||||
self._stalker_texture = new_texture
|
||||
|
|
|
@ -952,6 +952,9 @@ local function go_home(entity, sleep)
|
|||
entity.order = nil
|
||||
return
|
||||
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)
|
||||
local b = entity._bed
|
||||
|
@ -1331,7 +1334,7 @@ local function do_work (self)
|
|||
--mcl_log("Jobsite not valid")
|
||||
return false
|
||||
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!")
|
||||
return true
|
||||
else
|
||||
|
@ -1489,10 +1492,7 @@ local function do_activity (self)
|
|||
end
|
||||
|
||||
-- Only check in day or during thunderstorm but wandered_too_far code won't work
|
||||
local wandered_too_far = false
|
||||
if check_bed (self) then
|
||||
wandered_too_far = vector.distance(self.object:get_pos(),self._bed) > 50
|
||||
end
|
||||
local wandered_too_far = check_bed(self) and (vector.distance(self.object:get_pos(),self._bed) > 24)
|
||||
|
||||
if wandered_too_far then
|
||||
--mcl_log("Wandered too far! Return home ")
|
||||
|
@ -1503,7 +1503,7 @@ local function do_activity (self)
|
|||
do_work(self)
|
||||
elseif get_activity() == GATHERING then
|
||||
go_to_town_bell(self)
|
||||
else
|
||||
elseif self.order then
|
||||
mcl_log("No order, so remove it.")
|
||||
self.order = nil
|
||||
end
|
||||
|
@ -2110,8 +2110,8 @@ mcl_mobs.register_mob("mobs_mc:villager", {
|
|||
hp_min = 20,
|
||||
hp_max = 20,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 6.3,
|
||||
head_eye_height = 2.2,
|
||||
head_eye_height = 1.5,
|
||||
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
runaway = true,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.94, 0.3},
|
||||
|
|
|
@ -25,8 +25,8 @@ mcl_mobs.register_mob("mobs_mc:evoker", {
|
|||
xp_min = 10,
|
||||
xp_max = 10,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 6.3,
|
||||
head_eye_height = 2.2,
|
||||
head_eye_height = 1.5,
|
||||
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.95, 0.4},
|
||||
visual = "mesh",
|
||||
|
|
|
@ -34,8 +34,8 @@ mcl_mobs.register_mob("mobs_mc:illusioner", {
|
|||
"mcl_bows_bow.png",
|
||||
}, },
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 2.2,
|
||||
head_eye_height = 2.2,
|
||||
head_eye_height = 1.5,
|
||||
head_bone_position = vector.new( 0, 2.2, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
sounds = {
|
||||
-- TODO: more sounds
|
||||
|
|
|
@ -24,17 +24,17 @@ mcl_mobs.register_mob("mobs_mc:vindicator", {
|
|||
visual = "mesh",
|
||||
mesh = "mobs_mc_vindicator.b3d",
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 2.2,
|
||||
head_eye_height = 2.2,
|
||||
head_eye_height = 1.5,
|
||||
head_bone_position = vector.new( 0, 2.2, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 10,
|
||||
textures = {
|
||||
{
|
||||
"mobs_mc_vindicator.png",
|
||||
"blank.png", --no hat
|
||||
"default_tool_steelaxe.png",
|
||||
-- TODO: Glow when attacking (mobs_mc_vindicator.png)
|
||||
},
|
||||
},
|
||||
textures = {
|
||||
{
|
||||
"mobs_mc_vindicator.png",
|
||||
"blank.png", --no hat
|
||||
"default_tool_steelaxe.png",
|
||||
-- TODO: Glow when attacking (mobs_mc_vindicator.png)
|
||||
},
|
||||
},
|
||||
visual_size = {x=2.75, y=2.75},
|
||||
makes_footstep_sound = true,
|
||||
damage = 13,
|
||||
|
|
|
@ -40,7 +40,7 @@ mcl_mobs.register_mob("mobs_mc:villager_zombie", {
|
|||
visual = "mesh",
|
||||
mesh = "mobs_mc_villager_zombie.b3d",
|
||||
head_swivel = "Head_Control",
|
||||
bone_eye_height = 2.35,
|
||||
head_bone_position = vector.new( 0, 2.35, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 2,
|
||||
textures = {
|
||||
{"mobs_mc_zombie_butcher.png"},
|
||||
|
|
|
@ -13,14 +13,6 @@ local anti_troll = minetest.settings:get_bool("wither_anti_troll_measures", fals
|
|||
local WITHER_INIT_BOOM = 7
|
||||
local WITHER_MELEE_COOLDOWN = 3
|
||||
|
||||
local function atan(x)
|
||||
if not x or x ~= x then
|
||||
return 0
|
||||
else
|
||||
return math.atan(x)
|
||||
end
|
||||
end
|
||||
|
||||
--###################
|
||||
--################### WITHER
|
||||
--###################
|
||||
|
@ -96,7 +88,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
|||
distance = 60,
|
||||
},
|
||||
jump = true,
|
||||
jump_height = 10,
|
||||
jump_height = 5,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
dogshoot_switch = 1, -- unused
|
||||
|
@ -125,6 +117,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
|||
},
|
||||
harmed_by_heal = true,
|
||||
is_boss = true,
|
||||
see_through_opaque = false,
|
||||
extra_hostile = true,
|
||||
attack_exception = function(p)
|
||||
local ent = p:get_luaentity()
|
||||
|
@ -260,7 +253,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
|||
z = p.z - s.z
|
||||
}
|
||||
|
||||
local yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
||||
local yaw = (math.atan2(vec.z, vec.x) +math.pi/ 2) - self.rotate
|
||||
if p.x > s.x then yaw = yaw +math.pi end
|
||||
yaw = self:set_yaw( yaw, 0, dtime)
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ local wolf = {
|
|||
},
|
||||
makes_footstep_sound = true,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 3.5,
|
||||
head_eye_height = 1.1,
|
||||
head_eye_height = 0.5,
|
||||
head_bone_position = vector.new( 0, 3.5, 0 ), -- for minetest <= 5.8
|
||||
horizontal_head_height=0,
|
||||
curiosity = 3,
|
||||
head_yaw="z",
|
||||
|
|
|
@ -54,8 +54,8 @@ local zombie = {
|
|||
xp_min = 5,
|
||||
xp_max = 5,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 6.3,
|
||||
head_eye_height = 2.2,
|
||||
head_eye_height = 1.4,
|
||||
head_bone_position = vector.new( 0, 6.3, 0 ), -- for minetest <= 5.8
|
||||
curiosity = 7,
|
||||
head_pitch_multiplier=-1,
|
||||
breath_max = -1,
|
||||
|
@ -85,7 +85,7 @@ local zombie = {
|
|||
fear_height = 4,
|
||||
pathfinding = 1,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
group_attack = { "mobs_mc:zombie", "mobs_mc:baby_zombie", "mobs_mc:husk", "mobs_mc:baby_husk" },
|
||||
drops = drops_zombie,
|
||||
animation = {
|
||||
|
@ -110,6 +110,7 @@ mcl_mobs.register_mob("mobs_mc:zombie", zombie)
|
|||
|
||||
local baby_zombie = table.copy(zombie)
|
||||
baby_zombie.description = S("Baby Zombie")
|
||||
baby_zombie.head_eye_height = 0.8
|
||||
baby_zombie.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.98, 0.25}
|
||||
baby_zombie.xp_min = 12
|
||||
baby_zombie.xp_max = 12
|
||||
|
|
|
@ -34,6 +34,10 @@ local bamboo_def = {
|
|||
wield_image = "mcl_bamboo_bamboo_shoot.png",
|
||||
_mcl_blast_resistance = 1,
|
||||
_mcl_hardness = 1,
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
return mcl_bamboo.grow_bamboo(pos, true)
|
||||
end,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
|
@ -220,10 +224,12 @@ minetest.register_node(BAMBOO, bamboo_def)
|
|||
local bamboo_top = table.copy(bamboo_def)
|
||||
bamboo_top.groups = {not_in_creative_inventory = 1, handy = 1, axey = 1, choppy = 1, dig_by_piston = 1, plant = 1, non_mycelium_plant = 1, flammable = 3}
|
||||
bamboo_top.tiles = {"mcl_bamboo_endcap.png"}
|
||||
bamboo_top.drawtype = "plantlike_rooted" --"plantlike"
|
||||
--bamboo_top.paramtype2 = "meshoptions"
|
||||
--bamboo_top.param2 = 2
|
||||
-- bamboo_top.waving = 2
|
||||
|
||||
-- bamboo_top.drawtype = "plantlike_rooted" --"plantlike"
|
||||
bamboo_top.drawtype = "plantlike"
|
||||
bamboo_top.paramtype2 = "meshoptions"
|
||||
bamboo_top.param2 = 2
|
||||
bamboo_top.waving = 2
|
||||
bamboo_top.special_tiles = {{name = "mcl_bamboo_endcap.png"}}
|
||||
bamboo_top.nodebox = nil
|
||||
bamboo_top.selection_box = nil
|
||||
|
|
|
@ -44,6 +44,10 @@ mcl_bamboo.bamboo_index = {
|
|||
"mcl_bamboo:bamboo_2",
|
||||
"mcl_bamboo:bamboo_3",
|
||||
}
|
||||
mcl_bamboo.bamboo_set = {}
|
||||
for _,key in pairs(mcl_bamboo.bamboo_index) do
|
||||
mcl_bamboo.bamboo_set[key] = true
|
||||
end
|
||||
|
||||
function mcl_bamboo.is_bamboo(node_name)
|
||||
local index = table.indexof(mcl_bamboo.bamboo_index, node_name)
|
||||
|
@ -94,172 +98,84 @@ end
|
|||
--]]
|
||||
|
||||
function mcl_bamboo.grow_bamboo(pos, bonemeal_applied)
|
||||
local log = mcl_bamboo.mcl_log
|
||||
local node_above = minetest.get_node(vector.offset(pos, 0, 1, 0))
|
||||
mcl_bamboo.mcl_log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
|
||||
log("Grow bamboo called; bonemeal: " .. tostring(bonemeal_applied))
|
||||
|
||||
if not bonemeal_applied and mcl_bamboo.is_bamboo(node_above.name) ~= false then
|
||||
return false -- short circuit this function if we're trying to grow (std) the bamboo and it's not the top shoot.
|
||||
if not bonemeal_applied then
|
||||
-- Only allow natural growth at the top of the bamboo
|
||||
if mcl_bamboo.is_bamboo(node_above.name) ~= false then return false end
|
||||
|
||||
-- Don't perform natual growth in low light
|
||||
if minetest.get_node_light(pos) < 8 then return false end
|
||||
end
|
||||
if minetest.get_node_light(pos) < 8 then
|
||||
|
||||
-- Determine the location of soil
|
||||
local soil_pos
|
||||
soil_pos,a,b = mcl_util.trace_nodes(pos, -1, mcl_bamboo.bamboo_set, BAMBOO_MAX_HEIGHT - 1)
|
||||
|
||||
-- No soil found, return false so that bonemeal isn't used
|
||||
if not soil_pos then return false end
|
||||
log("Grow bamboo; soil found. ")
|
||||
|
||||
-- Find the first bamboo shoot and retrieve data about it
|
||||
local first_shoot = vector.offset(soil_pos, 0, 1, 0)
|
||||
local first_shoot_meta = minetest.get_meta(first_shoot)
|
||||
|
||||
-- Get or initialize bamboo height
|
||||
local height = (first_shoot_meta and first_shoot_meta:get_int("height", -1)) or -1
|
||||
if height == -1 then
|
||||
height = rand(BAM_MAX_HEIGHT_STPCHK + 1, BAM_MAX_HEIGHT_TOP + 1)
|
||||
first_shoot_meta:set_int("height", height)
|
||||
end
|
||||
log("Grow bamboo; height: " .. height)
|
||||
|
||||
-- Locate the bamboo tip
|
||||
local bamboo_tip,actual_height,bamboo_tip_node = mcl_util.trace_nodes(first_shoot, 1, mcl_bamboo.bamboo_set, height - 1)
|
||||
log("Current height: "..tostring(actual_height))
|
||||
|
||||
-- Short circuit growth if the bamboo is already finished growing
|
||||
if not bamboo_tip or not actual_height or actual_height >= height then
|
||||
log("Bamboo is already as large as it can grow")
|
||||
return false
|
||||
end
|
||||
|
||||
-- variables used in more than one spot.
|
||||
local first_shoot
|
||||
local chk_pos
|
||||
local soil_pos
|
||||
local node_name = ""
|
||||
local dist = 0
|
||||
local node_below
|
||||
-- -------------------
|
||||
-- Now that we are actually going to add nodes, initialize some more information
|
||||
local first_shoot_node_name = minetest.get_node(first_shoot).name
|
||||
|
||||
mcl_bamboo.mcl_log("Grow bamboo; checking for soil: ")
|
||||
-- the soil node below the bamboo.
|
||||
for py = -1, BAMBOO_SOIL_DIST, -1 do
|
||||
chk_pos = vector.offset(pos, 0, py, 0)
|
||||
node_name = minetest.get_node(chk_pos).name
|
||||
if mcl_bamboo.is_dirt(node_name) then
|
||||
soil_pos = chk_pos
|
||||
break
|
||||
end
|
||||
if mcl_bamboo.is_bamboo(node_name) == false then
|
||||
break
|
||||
end
|
||||
end
|
||||
-- requires knowing where the soil node is.
|
||||
if soil_pos == nil then
|
||||
return false -- returning false means don't use up the bonemeal.
|
||||
end
|
||||
|
||||
mcl_bamboo.mcl_log("Grow bamboo; soil found. ")
|
||||
local grow_amount = rand(1, GROW_DOUBLE_CHANCE)
|
||||
grow_amount = rand(1, GROW_DOUBLE_CHANCE)
|
||||
grow_amount = rand(1, GROW_DOUBLE_CHANCE) -- because yeah, not truly random, or even a good prng.
|
||||
grow_amount = rand(1, GROW_DOUBLE_CHANCE)
|
||||
local init_height = rand(BAM_MAX_HEIGHT_STPCHK + 1, BAM_MAX_HEIGHT_TOP + 1)
|
||||
mcl_bamboo.mcl_log("Grow bamboo; random height: " .. init_height)
|
||||
|
||||
node_name = ""
|
||||
|
||||
-- update: add randomized max height to first node's meta data.
|
||||
first_shoot = vector.offset(soil_pos, 0, 1, 0)
|
||||
local meta = minetest.get_meta(first_shoot)
|
||||
node_below = minetest.get_node(first_shoot).name
|
||||
|
||||
mcl_bamboo.mcl_log("Grow bamboo; checking height meta ")
|
||||
-- check the meta data for the first node, to see how high to make the stalk.
|
||||
if not meta then
|
||||
-- if no metadata, set the metadata!!!
|
||||
meta:set_int("height", init_height)
|
||||
end
|
||||
local height = meta:get_int("height", -1)
|
||||
mcl_bamboo.mcl_log("Grow bamboo; meta-height: " .. height)
|
||||
if height <= 10 then
|
||||
height = init_height
|
||||
meta:set_int("height", init_height)
|
||||
end
|
||||
|
||||
mcl_bamboo.mcl_log("Grow bamboo; height: " .. height)
|
||||
|
||||
-- Bonemeal: Grows the bamboo by 1-2 stems. (per the minecraft wiki.)
|
||||
-- If applying bonemeal, randomly grow two segments instead of one
|
||||
local grow_amount = 1
|
||||
if bonemeal_applied then
|
||||
-- handle applying bonemeal.
|
||||
for py = 1, BAM_MAX_HEIGHT_TOP do
|
||||
-- find the top node of bamboo.
|
||||
chk_pos = vector.offset(pos, 0, py, 0)
|
||||
node_name = minetest.get_node(chk_pos).name
|
||||
dist = vector.distance(soil_pos, chk_pos)
|
||||
if mcl_bamboo.is_bamboo(node_name) == false or node_name == BAMBOO_ENDCAP_NAME then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
mcl_bamboo.mcl_log("Grow bamboo; dist: " .. dist)
|
||||
|
||||
if node_name == BAMBOO_ENDCAP_NAME then
|
||||
-- prevent overgrowth
|
||||
return false
|
||||
end
|
||||
|
||||
-- check to see if we have a full stalk of bamboo.
|
||||
if dist >= height - 1 then
|
||||
if dist == height - 1 then
|
||||
-- equals top of the stalk before the cap
|
||||
if node_name == "air" then
|
||||
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap")
|
||||
minetest.set_node(vector.offset(chk_pos, 0, 1, 0), { name = BAMBOO_ENDCAP_NAME })
|
||||
return true -- returning true means use up the bonemeal.
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
-- okay, we're higher than the end cap, fail out.
|
||||
return false -- returning false means don't use up the bonemeal.
|
||||
end
|
||||
end
|
||||
|
||||
-- and now, the meat of the section... add bamboo to the stalk.
|
||||
-- at this point, we should be lower than the generated maximum height. ~ about height -2 or lower.
|
||||
if dist <= height - 2 then
|
||||
if node_name == "air" then
|
||||
-- here we can check to see if we can do up to 2 bamboo shoots onto the stalk
|
||||
mcl_bamboo.mcl_log("Grow bamboo; Placing bamboo.")
|
||||
minetest.set_node(chk_pos, { name = node_below })
|
||||
-- handle growing a second node.
|
||||
if grow_amount == 2 then
|
||||
chk_pos = vector.offset(chk_pos, 0, 1, 0)
|
||||
if minetest.get_node(chk_pos).name == "air" then
|
||||
mcl_bamboo.mcl_log("Grow bamboo; OOOH! It's twofer day!")
|
||||
minetest.set_node(chk_pos, { name = node_below })
|
||||
end
|
||||
end
|
||||
return true -- exit out with a success. We've added 1-2 nodes, per the wiki.
|
||||
end
|
||||
local rng = PcgRandom(minetest.hash_node_position(pos) + minetest.get_us_time())
|
||||
if rng:next(1, GROW_DOUBLE_CHANGE) == 1 then
|
||||
grow_amount = 2
|
||||
end
|
||||
end
|
||||
log("Growing up to "..grow_amount.." segments")
|
||||
|
||||
-- Non-Bonemeal growth.
|
||||
for py = 1, BAM_MAX_HEIGHT_TOP do
|
||||
-- Find the topmost node above the stalk, and check it for "air"
|
||||
chk_pos = vector.offset(pos, 0, py, 0)
|
||||
node_below = minetest.get_node(pos).name
|
||||
node_name = minetest.get_node(chk_pos).name
|
||||
dist = vector.distance(soil_pos, chk_pos)
|
||||
|
||||
if node_name ~= "air" and mcl_bamboo.is_bamboo(node_name) == false then
|
||||
break
|
||||
-- Perform bamboo growth
|
||||
for i = 1,grow_amount do
|
||||
-- Check for air to grow into
|
||||
local bamboo_tip_node = minetest.get_node(bamboo_tip)
|
||||
if not bamboo_tip_node or bamboo_tip_node.name ~= "air" then
|
||||
-- Something is blocking growth, stop and signal that use bonemeal has been used if at least on segment has grown
|
||||
return i ~= 1
|
||||
end
|
||||
|
||||
-- stop growing check. ie, handle endcap placement.
|
||||
if dist >= height - 1 then
|
||||
local above_node_name = minetest.get_node(vector.offset(chk_pos, 0, 1, 0)).name
|
||||
if node_name == "air" and above_node_name == "air" then
|
||||
if height - 1 == dist then
|
||||
mcl_bamboo.mcl_log("Grow bamboo; Placing endcap")
|
||||
minetest.set_node(chk_pos, { name = BAMBOO_ENDCAP_NAME })
|
||||
end
|
||||
end
|
||||
break
|
||||
if actual_height + 1 == height then
|
||||
-- This is the end cap
|
||||
minetest.set_node(bamboo_tip, { name = BAMBOO_ENDCAP_NAME })
|
||||
return true
|
||||
else
|
||||
-- This isn't the end cap, add a bamboo segment
|
||||
minetest.set_node(bamboo_tip, { name = first_shoot_node_name })
|
||||
actual_height = actual_height + 1
|
||||
end
|
||||
|
||||
-- handle regular node placement.
|
||||
-- find the air node above the top shoot. place a node. And then, if short enough,
|
||||
-- check for second node placement.
|
||||
if node_name == "air" then
|
||||
mcl_bamboo.mcl_log("Grow bamboo; dist: " .. dist)
|
||||
mcl_bamboo.mcl_log("Grow bamboo; Placing bamboo.")
|
||||
minetest.set_node(chk_pos, { name = node_below })
|
||||
-- handle growing a second node. (1 in 32 chance.)
|
||||
if grow_amount == 2 and dist <= height - 2 then
|
||||
chk_pos = vector.offset(chk_pos, 0, 1, 0)
|
||||
if minetest.get_node(chk_pos).name == "air" then
|
||||
mcl_bamboo.mcl_log("Grow bamboo; OOOH! It's twofer day!")
|
||||
minetest.set_node(chk_pos, { name = node_below })
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
bamboo_tip = vector.offset(bamboo_tip, 0, 1, 0)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Add Groups function, courtesy of Warr1024.
|
||||
|
|
|
@ -2,4 +2,4 @@ name = mcl_beds
|
|||
author = BlockMen
|
||||
description =
|
||||
depends = playerphysics
|
||||
optional_depends = mcl_sounds, mcl_worlds, mcl_wool, mcl_dye, mcl_explosions, mcl_weather, mcl_spawn, doc, mesecons
|
||||
optional_depends = mcl_sounds, mcl_worlds, mcl_wool, mcl_dye, mcl_explosions, mcl_weather, mcl_spawn, doc, mesecons, mesecons_mvps
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
# Bone meal API
|
||||
Bonemealing callbacks and particle functions.
|
||||
|
||||
|
||||
## _on_bone_meal(itemstack, placer, pointed_thing)
|
||||
The bone meal API provides a callback definition that nodes can use to
|
||||
register a handler that is executed when a bone meal item is used on it.
|
||||
|
||||
Nodes that wish to use the bone meal API should in their node registration
|
||||
define a callback handler named `_on_bone_meal`.
|
||||
|
||||
Note that by registering the callback handler, the node declares that bone
|
||||
meal can be used on it and as a result, when the user is not in creative
|
||||
mode, the used bone meal is spent and taken from the itemstack passed to
|
||||
the `on_place()` handler of the bone meal item used regardless of whether
|
||||
the bone meal had an effect on the node and regardless of the result of
|
||||
the callback handler.
|
||||
|
||||
It is for all intents and purposes up to the callback defined in the node to
|
||||
decide how to handle the specific effect that bone meal has on that node.
|
||||
|
||||
The `_on_bone_meal` callback handler is a
|
||||
|
||||
`function(itemstack, placer, pointed_thing)`
|
||||
|
||||
Its arguments are:
|
||||
* `itemstack`: the stack of bonem eal being applied
|
||||
* `placer`: ObjectRef of the player who aplied the bone meal, can be nil!
|
||||
* `pointed_thing`: exact pointing location (see Minetest API), where the
|
||||
bone meal is applied
|
||||
|
||||
The return value of the handler function indicates if the bonemealing had
|
||||
its intended effect. If `true`, 'bone meal particles' are spawned at the
|
||||
position of the bonemealed node.
|
||||
|
||||
The `on_place` code in the bone meal item will spawn bone meal particles and
|
||||
decrease the bone meal itemstack if the handler returned `true` and the
|
||||
`placer` is not in creative mode.
|
||||
|
||||
|
||||
## mcl_bone_meal.add_bone_meal_particle(pos, def)
|
||||
Spawns standard or custom bone meal particles.
|
||||
* `pos`: position, is ignored if you define def.minpos and def.maxpos
|
||||
* `def`: (optional) particle definition; see minetest.add_particlespawner()
|
||||
for more details.
|
||||
|
||||
## mcl_bone_meal.use_bone_meal(itemstack, placer, pointed_thing)
|
||||
For use in on_rightclick handlers that need support bone meal processing in addition
|
||||
to other behaviors. Before calling, verify that the player is wielding bone meal.
|
||||
* `itemstack`: The stack of bone meal being used
|
||||
* `placer`: ObjectRef of the player who aplied the bone meal, can be nil!
|
||||
* `pointed_thing`: exact pointing location (see Minetest API), where the
|
||||
bone meal is applied
|
||||
|
||||
Returns itemstack with one bone meal consumed if not in creative mode.
|
||||
|
||||
# Legacy API
|
||||
The bone meal API also provides a legacy compatibility function. This
|
||||
function is not meant to be continued and callers should migrate to the
|
||||
newer bonemealing API.
|
||||
|
||||
## mcl_bone_meal.register_on_bone_meal_apply(function(pointed_thing, placer))
|
||||
Called when the bone meal is applied anywhere.
|
||||
* `pointed_thing`: exact pointing location (see Minetest API), where the
|
||||
bone meal is applied
|
||||
* `placer`: ObjectRef of the player who aplied the bone meal, can be nil!
|
||||
This function is deprecated and will be removed at some time in the future.
|
||||
Bone meal is not consumed unless the provided function returns true.
|
||||
|
||||
## mcl_dye.add_bone_meal_particle(pos, def)
|
||||
## mcl_dye.register_on_bone_meal_apply(function(pointed_thing, user))
|
||||
These shims in mcl_dye that point to corresponding legacy compatibility
|
||||
functions in mcl_bone_meal remain for legacy callers that have not yet been
|
||||
updated to the new API. These shims will be removed at some time in the
|
||||
future.
|
|
@ -0,0 +1,156 @@
|
|||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local longdesc = S(
|
||||
"Bone meal is a white dye and also useful as a fertilizer to " ..
|
||||
"speed up the growth of many plants."
|
||||
)
|
||||
local usagehelp = S(
|
||||
"Rightclick a sheep to turn its wool white. Rightclick a plant " ..
|
||||
"to speed up its growth. Note that not all plants can be " ..
|
||||
"fertilized like this. When you rightclick a grass block, tall " ..
|
||||
"grass and flowers will grow all over the place."
|
||||
)
|
||||
|
||||
mcl_bone_meal = {}
|
||||
|
||||
-- Bone meal particle API:
|
||||
|
||||
--- Spawns bone meal particles.
|
||||
-- pos: where the particles spawn
|
||||
-- def: particle spawner parameters, see minetest.add_particlespawner() for
|
||||
-- details on these parameters.
|
||||
--
|
||||
function mcl_bone_meal.add_bone_meal_particle(pos, def)
|
||||
def = def or {}
|
||||
minetest.add_particlespawner({
|
||||
amount = def.amount or 10,
|
||||
time = def.time or 0.1,
|
||||
minpos = def.minpos or vector.subtract(pos, 0.5),
|
||||
maxpos = def.maxpos or vector.add(pos, 0.5),
|
||||
minvel = def.minvel or vector.new(-0.01, 0.01, -0.01),
|
||||
maxvel = def.maxvel or vector.new(0.01, 0.01, 0.01),
|
||||
minacc = def.minacc or vector.new(0, 0, 0),
|
||||
maxacc = def.maxacc or vector.new(0, 0, 0),
|
||||
minexptime = def.minexptime or 1,
|
||||
maxexptime = def.maxexptime or 4,
|
||||
minsize = def.minsize or 0.7,
|
||||
maxsize = def.maxsize or 2.4,
|
||||
texture = "mcl_particles_bonemeal.png^[colorize:#00EE00:125",
|
||||
glow = def.glow or 1,
|
||||
})
|
||||
end
|
||||
|
||||
-- Begin legacy bone meal API.
|
||||
--
|
||||
-- Compatibility code for legacy users of the old bone meal API.
|
||||
-- This code will be removed at some time in the future.
|
||||
--
|
||||
mcl_bone_meal.bone_meal_callbacks = {}
|
||||
|
||||
-- Shims for the old API are still available in mcl_dye and defer to
|
||||
-- the real functions in mcl_bone_meal.
|
||||
--
|
||||
function mcl_bone_meal.register_on_bone_meal_apply(func)
|
||||
minetest.log("warning", "register_on_bone_meal_apply(func) is deprecated. Read mcl_bone_meal/API.md!")
|
||||
local lines = string.split(debug.traceback(),"\n")
|
||||
for _,line in ipairs(lines) do
|
||||
minetest.log("warning",line)
|
||||
end
|
||||
table.insert(mcl_bone_meal.bone_meal_callbacks, func)
|
||||
end
|
||||
|
||||
-- Legacy registered users of the old API are handled through this function.
|
||||
--
|
||||
local function legacy_apply_bone_meal(pointed_thing, placer)
|
||||
-- Legacy API support
|
||||
local callbacks = mcl_bone_meal.bone_meal_callbacks
|
||||
for i = 1,#callbacks do
|
||||
if callbacks[i](pointed_thing, placer) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
-- End legacy bone meal API
|
||||
|
||||
function mcl_bone_meal.use_bone_meal(itemstack, placer, pointed_thing)
|
||||
local positions = {pointed_thing.under, pointed_thing.above}
|
||||
for i = 1,2 do
|
||||
local pos = positions[i]
|
||||
|
||||
-- Check protection
|
||||
if mcl_util.check_area_protection(pos, pointed_thing.above, placer) then return false end
|
||||
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
local success = false
|
||||
local consume
|
||||
|
||||
-- If the pointed node can be bonemealed, let it handle the processing.
|
||||
if ndef and ndef._on_bone_meal then
|
||||
success = ndef._on_bone_meal(itemstack, placer, {under = pos, above = vector.offset(pos, 0, 1, 0)})
|
||||
consume = true
|
||||
else
|
||||
-- Otherwise try the legacy API.
|
||||
success = legacy_apply_bone_meal(pointed_thing, placer)
|
||||
consume = success
|
||||
end
|
||||
|
||||
-- Take the item
|
||||
if consume then
|
||||
-- Particle effects
|
||||
mcl_bone_meal.add_bone_meal_particle(pos)
|
||||
|
||||
if not placer or not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if success then return itemstack end
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_craftitem("mcl_bone_meal:bone_meal", {
|
||||
description = S("Bone Meal"),
|
||||
_tt_help = S("Speeds up plant growth"),
|
||||
_doc_items_longdesc = longdesc,
|
||||
_doc_items_usagehelp = usagehelp,
|
||||
inventory_image = "mcl_bone_meal.png",
|
||||
groups = {craftitem=1},
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local node = minetest.get_node(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
|
||||
-- Use pointed node's on_rightclick function first, if present.
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
if ndef and ndef.on_rightclick then
|
||||
local new_stack = mcl_util.call_on_rightclick(itemstack, placer, pointed_thing)
|
||||
if new_stack and new_stack ~= itemstack then return new_stack end
|
||||
end
|
||||
end
|
||||
|
||||
return mcl_bone_meal.use_bone_meal(itemstack, placer, pointed_thing)
|
||||
end,
|
||||
_on_dispense = function(itemstack, pos, droppos, dropnode, dropdir)
|
||||
local pointed_thing
|
||||
if dropnode.name == "air" then
|
||||
pointed_thing = {above = droppos, under = vector.offset(droppos, 0, -1 ,0)}
|
||||
else
|
||||
pointed_thing = {above = pos, under = droppos}
|
||||
end
|
||||
|
||||
return mcl_bone_meal.use_bone_meal(itemstack, nil, pointed_thing)
|
||||
end,
|
||||
_dispense_into_walkable = true
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_bone_meal:bone_meal 3",
|
||||
recipe = {{"mcl_mobitems:bone"}},
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=Knochenmehl
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Knochenmehl ist ein weißer Farbstoff und auch nützlich als Dünger, um das Wachstum vieler Pflanzen zu beschleunigen.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Rechtsklicken Sie auf ein Schaf, um die Wolle weiß einzufärben. Rechtsklicken Sie auf eine Pflanze, um ihr Wachstum zu beschleunigen. Beachten Sie, dass nicht alle Pflanzen darauf ansprechen. Benutzen Sie es auf einem Grasblock, wächst viel hohes Gras und vielleicht auch ein paar Blumen.
|
||||
Speeds up plant growth=Beschleunigt Pflanzenwachstum
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=Harina de hueso
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La harina de hueso es un tinte blanco y también es útil como fertilizante para acelerar el crecimiento de muchas plantas.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=RHaga clic derecho en una oveja para volver su lana blanca. Haga clic derecho en una planta para acelerar su crecimiento. Tenga en cuenta que no todas las plantas pueden ser fertilizadas de esta manera. Cuando haces clic derecho en un bloque de hierba, crecerán hierba alta y flores por todo el lugar.
|
||||
Speeds up plant growth=Acelera el crecimiento de las plantas
|
|
@ -0,0 +1,6 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=Farine d'Os
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La farine d'os est une teinture blanche et est également utile comme engrais pour accélérer la croissance de nombreuses plantes.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=
|
||||
Cliquez avec le bouton droit sur un mouton pour blanchir sa laine. Cliquez avec le bouton droit sur une plante pour accélérer sa croissance. Cependant, toutes les plantes ne peuvent pas être fertilisées de cette manière. Lorsque vous cliquez avec le bouton droit sur un bloc d'herbe, les hautes herbes et les fleurs poussent autour.
|
||||
Speeds up plant growth=Accélère la croissance des plantes
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=Mączka kostna
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Mączka kostna to biała farba i przydatny nawóz, który przyspiesza rośnięcie wielu roślin.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Kliknij prawym na owcę, aby wybielić jej wełnę. Kliknij prawym na roślinę aby przyspieszyć jej wzrost. Zważ, że nie na wszystkie rośliny to tak działa. Gdy klikniesz prawym na blok trawy, wysoka trawa wyrośnie wokół.
|
||||
Speeds up plant growth=Przyspiesza wzrost roślin
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=Костная мука
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Костная мука является белым красителем. Она также полезна в качестве удобрения, чтобы увеличить скорость роста многих растений.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Кликните правой по овце, чтобы сделать её шерсть белой. Кликните правой по растению, чтобы ускорить его рост. Имейте в виду, что не все растения можно удобрять таким способом. Если вы кликнете по травяному блоку, то на этом месте вырастет высокая трава и цветы.
|
||||
Speeds up plant growth=Ускоряет рост растений
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=骨粉
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=骨粉是一種白色染料,也可作為肥料,加速許多植物的生長。
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=右鍵點擊一隻羊,使其羊毛變白。右鍵點擊一株植物以加快其生長速度。注意,不是所有的植物都能像這樣施肥。當你右鍵點擊一個草方時,高高的草和花會到處生長。
|
||||
Speeds up plant growth=加速植物生長
|
|
@ -0,0 +1,5 @@
|
|||
# textdomain: mcl_bone_meal
|
||||
Bone Meal=
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=
|
||||
Speeds up plant growth=
|
|
@ -0,0 +1,3 @@
|
|||
name = mcl_bone_meal
|
||||
description = Bone meal can be used as a fertilizer and as a dye.
|
||||
author = kabou, teknomunk
|
Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 165 B |
|
@ -257,10 +257,10 @@ function ARROW_ENTITY.on_step(self, dtime)
|
|||
mcl_burning.set_on_fire(obj, 5)
|
||||
end
|
||||
if not self._in_player and not self._blocked then
|
||||
obj:punch(self.object, 1.0, {
|
||||
full_punch_interval=1.0,
|
||||
damage_groups={fleshy=self._damage},
|
||||
}, self.object:get_velocity())
|
||||
mcl_util.deal_damage(obj, self._damage, {type = "arrow", source = self._shooter, direct = self.object})
|
||||
if self._extra_hit_func then
|
||||
self._extra_hit_func(obj)
|
||||
end
|
||||
if obj:is_player() then
|
||||
if not mcl_shields.is_blocking(obj) then
|
||||
local placement
|
||||
|
|
|
@ -168,7 +168,7 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
|
|||
itemstack:get_meta():set_string("active", "true")
|
||||
return itemstack
|
||||
end,
|
||||
groups = {weapon=1,weapon_ranged=1,bow=1,enchantability=1},
|
||||
groups = {weapon=1,weapon_ranged=1,bow=1,cannot_block=1,enchantability=1},
|
||||
_mcl_uses = 385,
|
||||
})
|
||||
|
||||
|
@ -216,7 +216,7 @@ for level=0, 2 do
|
|||
wield_scale = mcl_vars.tool_wield_scale,
|
||||
stack_max = 1,
|
||||
range = 0, -- Pointing range to 0 to prevent punching with bow :D
|
||||
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, enchantability=1},
|
||||
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, cannot_block=1, enchantability=1},
|
||||
-- Trick to disable digging as well
|
||||
on_use = function() return end,
|
||||
on_drop = function(itemstack, dropper, pos)
|
||||
|
|
|
@ -158,7 +158,7 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
|
|||
itemstack:get_meta():set_string("active", "true")
|
||||
return itemstack
|
||||
end,
|
||||
groups = {weapon=1,weapon_ranged=1,crossbow=1,enchantability=1},
|
||||
groups = {weapon=1,weapon_ranged=1,crossbow=1,cannot_block=1,enchantability=1},
|
||||
_mcl_uses = 326,
|
||||
})
|
||||
|
||||
|
@ -193,7 +193,7 @@ S("The speed and damage of the arrow increases the longer you charge. The regula
|
|||
itemstack:get_meta():set_string("active", "true")
|
||||
return itemstack
|
||||
end,
|
||||
groups = {weapon=1,weapon_ranged=1,crossbow=1,enchantability=1,not_in_creative_inventory=1},
|
||||
groups = {weapon=1,weapon_ranged=1,crossbow=1,cannot_block=1,enchantability=1,not_in_creative_inventory=1},
|
||||
_mcl_uses = 326,
|
||||
})
|
||||
|
||||
|
@ -238,7 +238,7 @@ for level=0, 2 do
|
|||
wield_scale = mcl_vars.tool_wield_scale,
|
||||
stack_max = 1,
|
||||
range = 0, -- Pointing range to 0 to prevent punching with bow :D
|
||||
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, bow=1, enchantability=1},
|
||||
groups = {not_in_creative_inventory=1, not_in_craft_guide=1, cannot_block=1, bow=1, enchantability=1},
|
||||
-- Trick to disable digging as well
|
||||
on_use = function() return end,
|
||||
on_drop = function(itemstack, dropper, pos)
|
||||
|
|
|
@ -2,29 +2,34 @@ local S = minetest.get_translator(minetest.get_current_modname())
|
|||
|
||||
mcl_cocoas = {}
|
||||
|
||||
-- Place cocoa
|
||||
local function cocoa_place(itemstack, placer, pt, plantname)
|
||||
--- Place a cocoa pod.
|
||||
-- Attempt to place a cocoa pod on a jungle tree. Checks if attachment
|
||||
-- point is a jungle tree and sets the correct orientation of the stem.
|
||||
--
|
||||
function mcl_cocoas.place(itemstack, placer, pt, plantname)
|
||||
-- check if pointing at a node
|
||||
if not pt or pt.type ~= "node" then
|
||||
return
|
||||
end
|
||||
|
||||
local under = minetest.get_node(pt.under)
|
||||
local node = minetest.get_node(pt.under)
|
||||
|
||||
-- return if any of the nodes are not registered
|
||||
if not minetest.registered_nodes[under.name] then
|
||||
local def = minetest.registered_nodes[node.name]
|
||||
if not def then
|
||||
return
|
||||
end
|
||||
|
||||
-- Am I right-clicking on something that has a custom on_rightclick set?
|
||||
if placer and not placer:get_player_control().sneak then
|
||||
if minetest.registered_nodes[under.name] and minetest.registered_nodes[under.name].on_rightclick then
|
||||
return minetest.registered_nodes[under.name].on_rightclick(pt.under, under, placer, itemstack) or itemstack
|
||||
if def and def.on_rightclick then
|
||||
local new_stack = mcl_util.call_on_rightclick(itemstack, placer, pt)
|
||||
if new_stack and new_stack ~= itemstack then return new_stack end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if pointing at jungle tree
|
||||
if under.name ~= "mcl_core:jungletree"
|
||||
if node.name ~= "mcl_core:jungletree"
|
||||
or minetest.get_node(pt.above).name ~= "air" then
|
||||
return
|
||||
end
|
||||
|
@ -39,9 +44,7 @@ local function cocoa_place(itemstack, placer, pt, plantname)
|
|||
|
||||
-- Add the node, set facedir and remove 1 item from the itemstack
|
||||
minetest.set_node(pt.above, {name = plantname, param2 = minetest.dir_to_facedir(clickdir)})
|
||||
|
||||
minetest.sound_play("default_place_node", {pos = pt.above, gain = 1.0}, true)
|
||||
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
@ -49,111 +52,119 @@ local function cocoa_place(itemstack, placer, pt, plantname)
|
|||
return itemstack
|
||||
end
|
||||
|
||||
-- Attempts to grow a cocoa at pos, returns true when grown, returns false if there's no cocoa
|
||||
-- or it is already at full size
|
||||
--- Grows cocoa pod one size larger.
|
||||
-- Attempts to grow a cocoa at pos, returns true when grown, returns false
|
||||
-- if there's no cocoa or it is already at full size.
|
||||
--
|
||||
function mcl_cocoas.grow(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "mcl_cocoas:cocoa_1" then
|
||||
minetest.set_node(pos, {name = "mcl_cocoas:cocoa_2", param2 = node.param2})
|
||||
elseif node.name == "mcl_cocoas:cocoa_2" then
|
||||
minetest.set_node(pos, {name = "mcl_cocoas:cocoa_3", param2 = node.param2})
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
return false
|
||||
return true
|
||||
end
|
||||
|
||||
-- Cocoa definition
|
||||
-- 1st stage
|
||||
local crop_def = {
|
||||
description = S("Premature Cocoa Pod"),
|
||||
_doc_items_create_entry = true,
|
||||
_doc_items_longdesc = S("Cocoa pods grow on the side of jungle trees in 3 stages."),
|
||||
drawtype = "mesh",
|
||||
mesh = "mcl_cocoas_cocoa_stage_0.obj",
|
||||
tiles = {"mcl_cocoas_cocoa_stage_0.png"},
|
||||
use_texture_alpha = minetest.features.use_texture_alpha_string_modes and "clip" or true,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
paramtype2 = "facedir",
|
||||
walkable = true,
|
||||
drop = "mcl_cocoas:cocoa_beans",
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.125, -0.0625, 0.1875, 0.125, 0.25, 0.4375}, -- Pod
|
||||
-- only caller was mcl_dye, consider converting these into local functions.
|
||||
local cocoa_place = mcl_cocoas.place
|
||||
local cocoa_grow = mcl_cocoas.grow
|
||||
|
||||
-- Cocoa pod variant definitions.
|
||||
--[[ TODO: Use a mesh for cocoas for perfect texture compability. ]]
|
||||
local podinfo = {
|
||||
{ desc = S("Premature Cocoa Pod"),
|
||||
longdesc = S("Cocoa pods grow on the side of jungle trees in 3 stages."),
|
||||
tiles = {
|
||||
"mcl_cocoas_cocoa_stage_0.png",
|
||||
},
|
||||
n_box = {-0.125, -0.0625, 0.1875, 0.125, 0.25, 0.4375},
|
||||
s_box = {-0.125, -0.0625, 0.1875, 0.125, 0.5, 0.5 },
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.125, -0.0625, 0.1875, 0.125, 0.5, 0.5}, -- Pod
|
||||
{ desc = S("Medium Cocoa Pod"),
|
||||
tiles = {
|
||||
"mcl_cocoas_cocoa_stage_1.png",
|
||||
},
|
||||
n_box = {-0.1875, -0.1875, 0.0625, 0.1875, 0.25, 0.4375},
|
||||
s_box = {-0.1875, -0.1875, 0.0625, 0.1875, 0.5, 0.5 },
|
||||
},
|
||||
groups = {
|
||||
handy = 1, axey = 1,
|
||||
dig_by_water=1, destroy_by_lava_flow=1, dig_by_piston=1,
|
||||
attached_node_facedir=1,
|
||||
not_in_creative_inventory=1,
|
||||
cocoa=1
|
||||
},
|
||||
sounds = mcl_sounds.node_sound_wood_defaults(),
|
||||
on_rotate = false,
|
||||
_mcl_blast_resistance = 3,
|
||||
_mcl_hardness = 0.2,
|
||||
}
|
||||
|
||||
-- 2nd stage
|
||||
minetest.register_node("mcl_cocoas:cocoa_1", table.copy(crop_def))
|
||||
|
||||
crop_def.description = S("Medium Cocoa Pod")
|
||||
crop_def._doc_items_create_entry = false
|
||||
crop_def.groups.cocoa = 2
|
||||
crop_def.mesh = "mcl_cocoas_cocoa_stage_1.obj"
|
||||
crop_def.tiles = {"mcl_cocoas_cocoa_stage_1.png"}
|
||||
crop_def.collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.1875, -0.1875, 0.0625, 0.1875, 0.25, 0.4375}, -- Pod
|
||||
},
|
||||
}
|
||||
crop_def.selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.1875, -0.1875, 0.0625, 0.1875, 0.5, 0.5},
|
||||
{ desc = S("Mature Cocoa Pod"),
|
||||
longdesc = S("A mature cocoa pod grew on a jungle tree to its full size and it is ready to be harvested for cocoa beans. It won't grow any further."),
|
||||
tiles = {
|
||||
"mcl_cocoas_cocoa_stage_2.png",
|
||||
},
|
||||
n_box = {-0.25, -0.3125, -0.0625, 0.25, 0.25, 0.4375},
|
||||
s_box = {-0.25, -0.3125, -0.0625, 0.25, 0.5, 0.5 },
|
||||
},
|
||||
}
|
||||
|
||||
minetest.register_node("mcl_cocoas:cocoa_2", table.copy(crop_def))
|
||||
for i = 1, 3 do
|
||||
local def = {
|
||||
description = podinfo[i].desc,
|
||||
_doc_items_create_entry = true,
|
||||
_doc_items_longdesc = podinfo[i].longdesc,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
tiles = podinfo[i].tiles,
|
||||
use_texture_alpha = "clip",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
podinfo[i].n_box, -- Pod
|
||||
-- FIXME: This has a thickness of 0. Is this OK in Minetest?
|
||||
{ 0, 0.25, 0.25, 0, 0.5, 0.5 }, }, -- Stem
|
||||
},
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = podinfo[i].n_box
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = podinfo[i].s_box
|
||||
},
|
||||
groups = {
|
||||
handy = 1, axey = 1, attached_node_facedir = 1,
|
||||
dig_by_water = 1, destroy_by_lava_flow = 1, dig_by_piston = 1,
|
||||
cocoa = i, not_in_creative_inventory = 1,
|
||||
},
|
||||
sunlight_propagates = true,
|
||||
walkable = true,
|
||||
drop = "mcl_cocoas:cocoa_beans",
|
||||
sounds = mcl_sounds.node_sound_wood_defaults(),
|
||||
on_rotate = false,
|
||||
_mcl_blast_resistance = 3,
|
||||
_mcl_hardness = 0.2,
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
return cocoa_grow(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
-- Final stage
|
||||
crop_def.description = S("Mature Cocoa Pod")
|
||||
crop_def._doc_items_longdesc = S("A mature cocoa pod grew on a jungle tree to its full size and it is ready to be harvested for cocoa beans. It won't grow any further.")
|
||||
crop_def._doc_items_create_entry = true
|
||||
crop_def.groups.cocoa = 3
|
||||
crop_def.mesh = "mcl_cocoas_cocoa_stage_2.obj"
|
||||
crop_def.tiles = {"mcl_cocoas_cocoa_stage_2.png"}
|
||||
crop_def.collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.25, -0.3125, -0.0625, 0.25, 0.25, 0.4375}, -- Pod
|
||||
},
|
||||
}
|
||||
crop_def.selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.25, -0.3125, -0.0625, 0.25, 0.5, 0.5},
|
||||
},
|
||||
}
|
||||
crop_def.drop = "mcl_cocoas:cocoa_beans 3"
|
||||
minetest.register_node("mcl_cocoas:cocoa_3", table.copy(crop_def))
|
||||
if i == 2 then
|
||||
def._doc_items_longdesc = nil
|
||||
def._doc_items_create_entry = false
|
||||
end
|
||||
if i == 3 then
|
||||
def.drop = "mcl_cocoas:cocoa_beans 3"
|
||||
def._on_bone_mealing = nil
|
||||
end
|
||||
|
||||
minetest.register_node("mcl_cocoas:cocoa_" .. i, table.copy(def))
|
||||
end
|
||||
|
||||
minetest.register_craftitem("mcl_cocoas:cocoa_beans", {
|
||||
description = S("Cocoa Beans"),
|
||||
inventory_image = "mcl_cocoa_beans.png",
|
||||
_tt_help = S("Grows at the side of jungle trees"),
|
||||
_doc_items_longdesc = S("Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye."),
|
||||
_doc_items_usagehelp = S("Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa."),
|
||||
inventory_image = "mcl_cocoas_cocoa_beans.png",
|
||||
groups = {craftitem = 1, compostability = 65},
|
||||
_doc_items_longdesc = S("Cocoa beans can be used to plant cocoa, bake cookies or cract brown dye."),
|
||||
_doc_items_usagehelp = S("Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa."),
|
||||
description = S("Cocoa Beans"),
|
||||
stack_max = 64,
|
||||
groups = {
|
||||
craftitem = 1, compostability = 65,
|
||||
},
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
return cocoa_place(itemstack, placer, pointed_thing, "mcl_cocoas:cocoa_1")
|
||||
end,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=Kakaobohnen
|
||||
Grows at the side of jungle trees=Wächst an der Seite von Dschungelbäumen
|
||||
Cocoa beans can be used to plant cocoa pods, bake chocolate cookies or craft brown dye.=Kakaobohnen können benutzt werden, um Kakao anzupflanzen, Kekse zu backen oder braune Farbstoffe herzustellen.
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Rechtsklicken Sie an die Seite eines Dschungelbaumstamms (Dschungelholz), um eine junge Kakaoschote zu pflanzen.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Rechtsklicken Sie auf ein Schaf, um die Wolle braun einzufärben. Rechtsklicken Sie an die Seite eines Dschungelbaumstamms (Dschungelholz), um eine junge Kakaoschote zu pflanzen.
|
||||
Premature Cocoa Pod=Junge Kakaoschote
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=Kakaoschoten wachsen an der Seite von Dschungelbäumen in 3 Stufen.
|
||||
Medium Cocoa Pod=Mittelgroße Kakaoschote
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=Granos de cacao
|
||||
Grows at the side of jungle trees=Crece al lado de los árboles de la jungla
|
||||
Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye.=Los granos de cacao se pueden usar para plantar cacao, hornear galletas o hacer tintes marrones.
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Haga clic derecho en el costado del tronco de un árbol de la jungla para plantar un cacao joven.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Haga clic derecho en una oveja para convertir su lana en marrón. Haga clic derecho en el costado del tronco de un árbol de la jungla para plantar un cacao joven.
|
||||
Premature Cocoa Pod=Vaina de cacao prematura
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=Las vainas de cacao crecen al lado de los árboles de jungla en 3 etapas.
|
||||
Medium Cocoa Pod=Vaina de cacao mediana
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=Fèves de Cacao
|
||||
Grows at the side of jungle trees=Pousse à côté des arbres de la jungle
|
||||
Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye.=Les fèves de cacao peuvent être utilisées pour planter du cacao, faire des biscuits ou fabriquer de la teinture brune.
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Clic droit sur le côté d'un tronc d'arbre de la jungle (Bois Acajou) pour planter un jeune cacaoyer.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Faites un clic droit sur un mouton pour brunir sa laine. Clic droit sur le côté d'un tronc d'arbre de la jungle (Bois Acajou) pour planter un jeune cacao.
|
||||
Premature Cocoa Pod=Gousse de cacao prématurée
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=Les cabosses de cacao poussent sur le côté des arbres d'Acajou en 3 étapes.
|
||||
Medium Cocoa Pod=Gousse de cacao moyenne
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=Ziarna kakaowe
|
||||
Grows at the side of jungle trees=Rośnie na boku tropikalnych drzew
|
||||
Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye.=Ziarna kakaowe mogą być używane do sadzenia kakao, pieczenia ciasteczek lub robienia brązowego barwnika.
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Naciśnij prawym na boku tropikalnego pnia (Tropikalne drewno) aby zasadzić młode kakao.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Naciśnij prawym aby zafarbować wełnę owcy na brązowo. Naciśnij prawym na boku tropikalnego pnia (Tropikalne drewno) aby zasadzić młode kakao.
|
||||
Premature Cocoa Pod=Niedojrzała roślina kakao
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=Roślina kakao rośnie na bokach tropikalnych drzew w 3 etapach
|
||||
Medium Cocoa Pod=Średnio-dojrzała roślina kakao
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=Какао-бобы
|
||||
Grows at the side of jungle trees=Растут на стволах тропических деревьев
|
||||
Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye.=Какао-бобы можно использовать для посадки какао, выпечки печенья или изготовления коричневого красителя.
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Кликните правой по боковой части ствола тропического дерева, чтобы посадить молодое какао.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Кликните правой по овце, чтобы сделать её шерсть коричневой. Кликните правой по боковой части ствола тропического дерева, чтобы посадить молодое какао.
|
||||
Premature Cocoa Pod=Молодой стручок какао
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=Стручки какао растут на деревьях джунглей в 3 этапа.
|
||||
Medium Cocoa Pod=Средний стручок какао
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=可可豆
|
||||
Grows at the side of jungle trees=在叢林木側生長
|
||||
Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye.=可可豆可用於種植可可、烘烤餅乾或製作棕色染料。
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=右鍵點擊叢林木的一側,可以種植一個可可。
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=右鍵點擊一隻羊,使其羊毛變成褐色。右鍵點擊叢林木的一側,可以種植一個可可。
|
||||
Premature Cocoa Pod=成長中的可可豆莢(第1階段)
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=可可莢果分3個階段生長在叢林樹的側面。
|
||||
Medium Cocoa Pod=成長中的可可豆莢(第2階段)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Cocoa Beans=
|
||||
Grows at the side of jungle trees=
|
||||
Cocoa beans can be used to plant cocoa, bake cookies or craft brown dye.=
|
||||
Right click on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=
|
||||
Premature Cocoa Pod=
|
||||
Cocoa pods grow on the side of jungle trees in 3 stages.=
|
||||
Medium Cocoa Pod=
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mcl_cocoas
|
||||
description = Cocoa pods which grow at jungle trees. Does not include cocoa beans.
|
||||
depends = mcl_sounds, mcl_core
|
||||
optional_depends = doc
|
||||
depends = mcl_sounds, mcl_core, mcl_util
|
||||
optional_depends = doc
|
||||
|
|
|
@ -11,7 +11,7 @@ local composter_description = S(
|
|||
"Composter"
|
||||
)
|
||||
local composter_longdesc = S(
|
||||
"Composters can convert various organic items into bonemeal."
|
||||
"Composters can convert various organic items into bone meal."
|
||||
)
|
||||
local composter_usagehelp = S(
|
||||
"Use organic items on the composter to fill it with layers of compost. " ..
|
||||
|
@ -48,6 +48,41 @@ local vector_offset = vector.offset
|
|||
local is_protected = minetest.is_protected
|
||||
local record_protection_violation = minetest.record_protection_violation
|
||||
|
||||
--- Math and node swap during compost progression
|
||||
---@param pos Vector Position of the node
|
||||
---@param node node
|
||||
---@param chance integer Value of "compostability" group of inserted item
|
||||
local function composter_progress_chance(pos, node, chance)
|
||||
-- calculate leveling up chance
|
||||
local rand = math.random(0,100)
|
||||
if chance >= rand then
|
||||
-- get current compost level
|
||||
local level = registered_nodes[node.name]["_mcl_compost_level"]
|
||||
-- spawn green particles above new layer
|
||||
mcl_bone_meal.add_bone_meal_particle(vector_offset(pos, 0, level/8, 0))
|
||||
-- update composter block
|
||||
if level < 7 then
|
||||
level = level + 1
|
||||
else
|
||||
level = "ready"
|
||||
end
|
||||
swap_node(pos, {name = "mcl_composters:composter_" .. level})
|
||||
minetest.sound_play({name="default_grass_footstep", gain=0.4}, {
|
||||
pos = pos,
|
||||
gain= 0.4,
|
||||
max_hear_distance = 16,
|
||||
}, true)
|
||||
-- a full composter becomes ready for harvest after one second
|
||||
-- the block will get updated by the node timer callback set in node reg def
|
||||
if level == 7 then
|
||||
local timer = get_node_timer(pos)
|
||||
if not timer:is_started() then
|
||||
timer:start(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Fill the composter when rightclicked.
|
||||
--
|
||||
-- `on_rightclick` handler for composter blocks of all fill levels except
|
||||
|
@ -86,41 +121,6 @@ local function composter_add_item(pos, node, player, itemstack, pointed_thing)
|
|||
return itemstack
|
||||
end
|
||||
|
||||
--- Math and node swap during compost progression
|
||||
---@param pos Vector Position of the node
|
||||
---@param node node
|
||||
---@param chance integer Value of "compostability" group of inserted item
|
||||
function composter_progress_chance(pos, node, chance)
|
||||
-- calculate leveling up chance
|
||||
local rand = math.random(0,100)
|
||||
if chance >= rand then
|
||||
-- get current compost level
|
||||
local level = registered_nodes[node.name]["_mcl_compost_level"]
|
||||
-- spawn green particles above new layer
|
||||
mcl_dye.add_bone_meal_particle(vector_offset(pos, 0, level/8, 0))
|
||||
-- update composter block
|
||||
if level < 7 then
|
||||
level = level + 1
|
||||
else
|
||||
level = "ready"
|
||||
end
|
||||
swap_node(pos, {name = "mcl_composters:composter_" .. level})
|
||||
minetest.sound_play({name="default_grass_footstep", gain=0.4}, {
|
||||
pos = pos,
|
||||
gain= 0.4,
|
||||
max_hear_distance = 16,
|
||||
}, true)
|
||||
-- a full composter becomes ready for harvest after one second
|
||||
-- the block will get updated by the node timer callback set in node reg def
|
||||
if level == 7 then
|
||||
local timer = get_node_timer(pos)
|
||||
if not timer:is_started() then
|
||||
timer:start(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Update a full composter block to ready for harvesting.
|
||||
--
|
||||
-- `on_timer` handler. The timer is set in function 'composter_add_item'
|
||||
|
@ -203,7 +203,7 @@ end
|
|||
--
|
||||
minetest.register_node("mcl_composters:composter", {
|
||||
description = composter_description,
|
||||
_tt_help = S("Converts organic items into bonemeal"),
|
||||
_tt_help = S("Converts organic items into bone meal"),
|
||||
_doc_items_longdesc = composter_longdesc,
|
||||
_doc_items_usagehelp = composter_usagehelp,
|
||||
paramtype = "light",
|
||||
|
|
|
@ -4,4 +4,4 @@ Composters can convert various organic items into bonemeal.=Les composteurs peuv
|
|||
Use organic items on the composter to fill it with layers of compost. Every time an item is put in the composter, there is a chance that the composter adds another layer of compost. Some items have a bigger chance of adding an extra layer than other items. After filling up with 7 layers of compost, the composter is full. After a delay of approximately one second the composter becomes ready and bone meal can be retrieved from it. Right-clicking the composter takes out the bone meal empties the composter.=Utiliser des objets organiques sur le composteur pour le remplir de couches de compost. Chaque fois qu'un objet est mis dans le composteur, il y a une chance d'ajouter une nouvelle couche de compost au composteur. Certains objets ont une plus grande chance que d'autres d'ajouter une couche supplémentaire. Après l'avoir rempli de 7 couches de compost, le composteur est plein. Après un délai d'approximativement une seconde, le composteur est prêt et on peut récupérer la farine d'os. Cliquer droit le composteur permet de récupérer la farine d'os et de vider le composteur.
|
||||
filled=rempli
|
||||
ready for harvest=prêt pour la récolte
|
||||
Converts organic items into bonemeal=Convertit les objets organiques en farine d'os.
|
||||
Converts organic items into bone meal=Convertit les objets organiques en farine d'os.
|
||||
|
|
|
@ -4,4 +4,4 @@ Composters can convert various organic items into bonemeal.=コンポスター
|
|||
Use organic items on the composter to fill it with layers of compost. Every time an item is put in the composter, there is a chance that the composter adds another layer of compost. Some items have a bigger chance of adding an extra layer than other items. After filling up with 7 layers of compost, the composter is full. After a delay of approximately one second the composter becomes ready and bone meal can be retrieved from it. Right-clicking the composter takes out the bone meal empties the composter."=コンポスターに有機物を入れて、堆肥の層を作りましょう。コンポスターに有機物を入れるたびに、次の堆肥の層が追加されるチャンスが起きます。 追加される確率がより高くなっているアイテムもいくつかあります。 7層分の堆肥が充填されると、コンポスターは満杯となります。その約1秒後に、骨粉を取り出せる準備が完了します。右クリックして骨粉を取り出すと、コンポスターは空になります。
|
||||
filled=充足
|
||||
ready for harvest=収穫可能
|
||||
Converts organic items into bonemeal=有機物を骨粉に変える
|
||||
Converts organic items into bone meal=有機物を骨粉に変える
|
||||
|
|
|
@ -4,4 +4,4 @@ Composters can convert various organic items into bonemeal.=Компостер
|
|||
Use organic items on the composter to fill it with layers of compost. Every time an item is put in the composter, there is a chance that the composter adds another layer of compost. Some items have a bigger chance of adding an extra layer than other items. After filling up with 7 layers of compost, the composter is full. After a delay of approximately one second the composter becomes ready and bone meal can be retrieved from it. Right-clicking the composter takes out the bone meal empties the composter.=Используйте органические предметы на компостере, чтобы заполнить его слоями перегноя. Каждый раз когда в компостер попадает предмет, есть шанс что в компостере появится новый слой перегноя. Некоторые предметы имеют больший шанс на появление нового слоя. После заполнения 7 слоями перегноя, компостер можно опустошить, забрав из него костную муку. После задержки в одну секунду компостер будет готов и костная мука будет извлечена из него. Правым кликом по компостеру чтобы забрать костную муку.
|
||||
filled=заполнен
|
||||
ready for harvest=готов к сбору
|
||||
Converts organic items into bonemeal=Перерабатывает органику в костную муку
|
||||
Converts organic items into bone meal=Перерабатывает органику в костную муку
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# textdomain: mcl_composters
|
||||
Composter=
|
||||
Composters can convert various organic items into bonemeal.=
|
||||
Use organic items on the composter to fill it with layers of compost. Every time an item is put in the composter, there is a chance that the composter adds another layer of compost. Some items have a bigger chance of adding an extra layer than other items. After filling up with 7 layers of compost, the composter is full. After a delay of approximately one second the composter becomes ready and bone meal can be retrieved from it. Right-clicking the composter takes out the bone meal empties the composter.=
|
||||
Composters can convert various organic items into bone meal.=
|
||||
Use organic items on the composter to fill it with layers of compost. Every time an item is put in the composter, there is a chance that the composter adds another layer of compost. Some items have a bigger chance of adding an extra layer than other items. After filling up with 7 layers of compost, the composter is full. After a delay of approximately one second the composter becomes ready and bone meal can be retrieved from it. Right-clicking the composter takes out the bone meal empties the composter.=
|
||||
filled=
|
||||
ready for harvest=
|
||||
Converts organic items into bonemeal=
|
||||
Converts organic items into bone meal=
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = mcl_composters
|
||||
author = kabou
|
||||
description = Composters can convert various organic items into bonemeal.
|
||||
depends = mcl_core, mcl_sounds, mcl_dye, mcl_hoppers
|
||||
depends = mcl_core, mcl_sounds, mcl_bone_meal, mcl_hoppers
|
||||
optional_depends = doc
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -280,6 +280,14 @@ function mcl_core.register_sapling(subname, description, longdesc, tt_help, text
|
|||
nn == "mcl_core:podzol" or nn == "mcl_core:podzol_snow" or
|
||||
nn == "mcl_core:dirt" or nn == "mcl_core:mycelium" or nn == "mcl_core:coarse_dirt"
|
||||
end),
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local n = minetest.get_node(pos)
|
||||
-- Saplings: 45% chance to advance growth stage
|
||||
if math.random(1,100) <= 45 then
|
||||
return mcl_core.grow_sapling(pos, n)
|
||||
end
|
||||
end,
|
||||
node_placement_prediction = "",
|
||||
_mcl_blast_resistance = 0,
|
||||
_mcl_hardness = 0,
|
||||
|
|
|
@ -5,6 +5,8 @@ local modpath = minetest.get_modpath(modname)
|
|||
-- by debiankaios
|
||||
-- adapted for mcl2 by cora
|
||||
|
||||
local MAXIMUM_VINE_HEIGHT = 25
|
||||
|
||||
local wood_slab_groups = {handy = 1, axey = 1, material_wood = 1, wood_slab = 1}
|
||||
local wood_stair_groups = {handy = 1, axey = 1, material_wood = 1, wood_stairs = 1}
|
||||
|
||||
|
@ -16,21 +18,36 @@ function generate_crimson_tree(pos)
|
|||
minetest.place_schematic(pos,modpath.."/schematics/crimson_fungus_1.mts","random",nil,false,"place_center_x,place_center_z")
|
||||
end
|
||||
|
||||
function grow_vines(pos, moreontop ,vine, dir)
|
||||
function grow_vines(pos, moreontop, vine, dir)
|
||||
-- Sanity checks
|
||||
if dir == nil then dir = 1 end
|
||||
local n
|
||||
repeat
|
||||
pos = vector.offset(pos,0,dir,0)
|
||||
n = minetest.get_node(pos)
|
||||
if n.name == "air" then
|
||||
for i=0,math.max(moreontop,1) do
|
||||
if minetest.get_node(pos).name == "air" then
|
||||
minetest.set_node(vector.offset(pos,0,i*dir,0),{name=vine})
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
until n.name ~= "air" and n.name ~= vine
|
||||
if not moreontop or moreontop < 1 then return false end
|
||||
|
||||
local allowed_nodes = {}
|
||||
allowed_nodes[vine] = true
|
||||
|
||||
-- Find the root, tip and calculate height
|
||||
local root,_,root_node = mcl_util.trace_nodes(pos, -dir, allowed_nodes, MAXIMUM_VINE_HEIGHT)
|
||||
if not root then return false end
|
||||
local tip,height,tip_node = mcl_util.trace_nodes(vector.offset(root, 0, dir, 0), dir, allowed_nodes, MAXIMUM_VINE_HEIGHT)
|
||||
if not tip then return false end
|
||||
|
||||
local res = false
|
||||
for i = 1,moreontop do
|
||||
-- Check if we can grow into this position
|
||||
if height >= MAXIMUM_VINE_HEIGHT then return res end
|
||||
if tip_node.name ~= "air" then return res end
|
||||
|
||||
-- Update world map data
|
||||
minetest.set_node(tip, {name = vine})
|
||||
|
||||
-- Move to the next position and flag that growth has occured
|
||||
tip = vector.offset(tip, 0, dir, 0)
|
||||
tip_node = minetest.get_node(tip)
|
||||
height = height + 1
|
||||
res = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local nether_plants = {
|
||||
|
@ -83,17 +100,20 @@ minetest.register_node("mcl_crimson:warped_fungus", {
|
|||
light_source = 1,
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
node_placement_prediction = "",
|
||||
on_rightclick = function(pos, node, pointed_thing, player, itemstack)
|
||||
if pointed_thing:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then
|
||||
local nodepos = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
|
||||
if nodepos.name == "mcl_crimson:warped_nylium" or nodepos.name == "mcl_nether:netherrack" then
|
||||
local random = math.random(1, 5)
|
||||
if random == 1 then
|
||||
minetest.remove_node(pos)
|
||||
generate_warped_tree(pos)
|
||||
end
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local nodepos = minetest.get_node(vector.offset(pos, 0, -1, 0))
|
||||
|
||||
if nodepos.name == "mcl_crimson:warped_nylium" or nodepos.name == "mcl_nether:netherrack" then
|
||||
local random = math.random(1, 5)
|
||||
if random == 1 then
|
||||
minetest.remove_node(pos)
|
||||
generate_warped_tree(pos)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
|
@ -102,6 +122,12 @@ mcl_flowerpots.register_potted_flower("mcl_crimson:warped_fungus", {
|
|||
name = "warped_fungus",
|
||||
desc = S("Warped Fungus"),
|
||||
image = "mcl_crimson_warped_fungus.png",
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local n = has_nylium_neighbor(pointed_thing.under)
|
||||
if n then
|
||||
minetest.set_node(pointed_thing.under,n)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("mcl_crimson:twisting_vines", {
|
||||
|
@ -121,6 +147,9 @@ minetest.register_node("mcl_crimson:twisting_vines", {
|
|||
fixed = { -3/16, -0.5, -3/16, 3/16, 0.5, 3/16 },
|
||||
},
|
||||
node_placement_prediction = "",
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
return grow_vines(pointed_thing.under, math.random(1, 3),"mcl_crimson:twisting_vines")
|
||||
end,
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
local pn = clicker:get_player_name()
|
||||
if clicker:is_player() and minetest.is_protected(vector.offset(pos,0,1,0), pn or "") then
|
||||
|
@ -141,26 +170,28 @@ minetest.register_node("mcl_crimson:twisting_vines", {
|
|||
end
|
||||
|
||||
elseif clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
grow_vines(pos, math.random(1, 3),"mcl_crimson:twisting_vines")
|
||||
return mcl_bone_meal.use_bone_meal(itemstack, clicker, {under=pos, above=pos})
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local under = pointed_thing.under
|
||||
local above = pointed_thing.above
|
||||
local unode = minetest.get_node(under)
|
||||
local unode_def = minetest.registered_nodes[unode.name]
|
||||
|
||||
local above = pointed_thing.above
|
||||
local anode = minetest.get_node(above)
|
||||
local anode_def = minetest.registered_nodes[anode.name]
|
||||
|
||||
if under.y < above.y then
|
||||
minetest.set_node(above, {name = "mcl_crimson:twisting_vines"})
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
else
|
||||
if unode.name == "mcl_crimson:twisting_vines" then
|
||||
return minetest.registered_nodes[unode.name].on_rightclick(under, unode, placer, itemstack, pointed_thing)
|
||||
end
|
||||
elseif unode_def and unode_def.on_rightclick then
|
||||
return unode_def.on_rightclick(under, unode, placer, itemstack, pointed_thing)
|
||||
elseif anode_def and anode_def.on_rightclick then
|
||||
return unode_def.on_rightclick(above, anode, placer, itemstack, pointed_thing)
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
|
@ -168,7 +199,7 @@ minetest.register_node("mcl_crimson:twisting_vines", {
|
|||
local above = vector.offset(pos,0,1,0)
|
||||
local abovenode = minetest.get_node(above)
|
||||
minetest.node_dig(pos, node, digger)
|
||||
if abovenode.name == node.name and (not mcl_core.check_vines_supported(above, abovenode)) then
|
||||
if abovenode.name == node.name then
|
||||
minetest.registered_nodes[node.name].on_dig(above, node, digger)
|
||||
end
|
||||
end,
|
||||
|
@ -211,6 +242,9 @@ minetest.register_node("mcl_crimson:weeping_vines", {
|
|||
fixed = { -3/16, -0.5, -3/16, 3/16, 0.5, 3/16 },
|
||||
},
|
||||
node_placement_prediction = "",
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
return grow_vines(pointed_thing.under, math.random(1, 3),"mcl_crimson:weeping_vines", -1)
|
||||
end,
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
local pn = clicker:get_player_name()
|
||||
if clicker:is_player() and minetest.is_protected(vector.offset(pos,0,1,0), pn or "") then
|
||||
|
@ -231,26 +265,28 @@ minetest.register_node("mcl_crimson:weeping_vines", {
|
|||
end
|
||||
|
||||
elseif clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then
|
||||
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
grow_vines(pos, math.random(1, 3),"mcl_crimson:weeping_vines", -1)
|
||||
return mcl_bone_meal.use_bone_meal(itemstack, clicker, {under=pos, above=pos})
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local under = pointed_thing.under
|
||||
local above = pointed_thing.above
|
||||
local unode = minetest.get_node(under)
|
||||
local unode_def = minetest.registered_nodes[unode.name]
|
||||
|
||||
local above = pointed_thing.above
|
||||
local anode = minetest.get_node(above)
|
||||
local anode_def = minetest.registered_nodes[anode.name]
|
||||
|
||||
if under.y > above.y then
|
||||
minetest.set_node(above, {name = "mcl_crimson:weeping_vines"})
|
||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
else
|
||||
if unode.name == "mcl_crimson:weeping_vines" then
|
||||
return minetest.registered_nodes[unode.name].on_rightclick(under, unode, placer, itemstack, pointed_thing)
|
||||
end
|
||||
elseif unode_def and unode_def.on_rightclick then
|
||||
return unode_def.on_rightclick(under, unode, placer, itemstack, pointed_thing)
|
||||
elseif anode_def and anode_def.on_rightclick then
|
||||
return unode_def.on_rightclick(above, anode, placer, itemstack, pointed_thing)
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
|
@ -258,7 +294,7 @@ minetest.register_node("mcl_crimson:weeping_vines", {
|
|||
local below = vector.offset(pos,0,-1,0)
|
||||
local belownode = minetest.get_node(below)
|
||||
minetest.node_dig(pos, node, digger)
|
||||
if belownode.name == node.name and (not mcl_core.check_vines_supported(below, belownode)) then
|
||||
if belownode.name == node.name then
|
||||
minetest.registered_nodes[node.name].on_dig(below, node, digger)
|
||||
end
|
||||
end,
|
||||
|
@ -393,6 +429,11 @@ minetest.register_node("mcl_crimson:warped_nylium", {
|
|||
_mcl_hardness = 0.4,
|
||||
_mcl_blast_resistance = 0.4,
|
||||
_mcl_silk_touch_drop = true,
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
spread_nether_plants(pointed_thing.under,node)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
--Stem bark, stripped stem and bark
|
||||
|
@ -525,17 +566,21 @@ minetest.register_node("mcl_crimson:crimson_fungus", {
|
|||
fixed = { -3/16, -0.5, -3/16, 3/16, -2/16, 3/16 },
|
||||
},
|
||||
node_placement_prediction = "",
|
||||
on_rightclick = function(pos, node, pointed_thing, player)
|
||||
if pointed_thing:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then
|
||||
local nodepos = minetest.get_node(vector.offset(pos, 0, -1, 0))
|
||||
if nodepos.name == "mcl_crimson:crimson_nylium" or nodepos.name == "mcl_nether:netherrack" then
|
||||
local random = math.random(1, 5)
|
||||
if random == 1 then
|
||||
minetest.remove_node(pos)
|
||||
generate_crimson_tree(pos)
|
||||
end
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local nodepos = minetest.get_node(vector.offset(pos, 0, -1, 0))
|
||||
if nodepos.name == "mcl_crimson:crimson_nylium" or nodepos.name == "mcl_nether:netherrack" then
|
||||
local random = math.random(1, 5)
|
||||
if random == 1 then
|
||||
minetest.remove_node(pos)
|
||||
generate_crimson_tree(pos)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Failed to spread nylium
|
||||
return false
|
||||
end,
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
|
@ -682,6 +727,11 @@ minetest.register_node("mcl_crimson:crimson_nylium", {
|
|||
_mcl_hardness = 0.4,
|
||||
_mcl_blast_resistance = 0.4,
|
||||
_mcl_silk_touch_drop = true,
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
spread_nether_plants(pointed_thing.under,node)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
|
@ -723,19 +773,6 @@ minetest.register_craft({
|
|||
mcl_stairs.register_stair("crimson_hyphae_wood", "mcl_crimson:crimson_hyphae_wood", wood_stair_groups, false, S("Crimson Stair"))
|
||||
mcl_stairs.register_slab("crimson_hyphae_wood", "mcl_crimson:crimson_hyphae_wood", wood_slab_groups, false, S("Crimson Slab"))
|
||||
|
||||
mcl_dye.register_on_bone_meal_apply(function(pt,user)
|
||||
if not pt.type == "node" then return end
|
||||
local node = minetest.get_node(pt.under)
|
||||
if node.name == "mcl_nether:netherrack" then
|
||||
local n = has_nylium_neighbor(pt.under)
|
||||
if n then
|
||||
minetest.set_node(pt.under,n)
|
||||
end
|
||||
elseif node.name == "mcl_crimson:warped_nylium" or node.name == "mcl_crimson:crimson_nylium" then
|
||||
spread_nether_plants(pt.under,node)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Turn Crimson Nylium and Warped Nylium below solid block into Netherrack",
|
||||
nodenames = {"mcl_crimson:crimson_nylium","mcl_crimson:warped_nylium"},
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
# mcl_dye
|
||||
|
||||
# Bone meal API
|
||||
Callback and particle functions.
|
||||
|
||||
## mcl_dye.add_bone_meal_particle(pos, def)
|
||||
Spawns standard or custom bone meal particles.
|
||||
* `pos`: position, is ignored if you define def.minpos and def.maxpos
|
||||
* `def`: (optional) particle definition
|
||||
|
||||
## mcl_dye.register_on_bone_meal_apply(function(pointed_thing, user))
|
||||
Called when the bone meal is applied anywhere.
|
||||
* `pointed_thing`: exact pointing location (see Minetest API), where the bone meal is applied
|
||||
* `user`: ObjectRef of the player who aplied the bone meal, can be nil!
|
|
@ -15,7 +15,6 @@ mcl_dye = {}
|
|||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
|
||||
local math = math
|
||||
local string = string
|
||||
|
||||
-- Base color groups:
|
||||
|
@ -115,328 +114,23 @@ for _, row in pairs(dyes) do
|
|||
})
|
||||
end
|
||||
|
||||
-- Bone meal code to be moved into its own mod.
|
||||
-- Legacy support for things now in mcl_bone_meal.
|
||||
-- These shims will at some time in the future be removed.
|
||||
--
|
||||
function mcl_dye.add_bone_meal_particle(pos, def)
|
||||
if not def then
|
||||
def = {}
|
||||
minetest.log("warning", "mcl_dye.add_bone_meal_particles() is deprecated. Read mcl_bone_meal/API.md!")
|
||||
local lines = string.split(debug.traceback(),"\n")
|
||||
for _,line in ipairs(lines) do
|
||||
minetest.log("warning",line)
|
||||
end
|
||||
minetest.add_particlespawner({
|
||||
amount = def.amount or 10,
|
||||
time = def.time or 0.1,
|
||||
minpos = def.minpos or vector.subtract(pos, 0.5),
|
||||
maxpos = def.maxpos or vector.add(pos, 0.5),
|
||||
minvel = def.minvel or vector.new(-0.01, 0.01, -0.01),
|
||||
maxvel = def.maxvel or vector.new(0.01, 0.01, 0.01),
|
||||
minacc = def.minacc or vector.new(0, 0, 0),
|
||||
maxacc = def.maxacc or vector.new(0, 0, 0),
|
||||
minexptime = def.minexptime or 1,
|
||||
maxexptime = def.maxexptime or 4,
|
||||
minsize = def.minsize or 0.7,
|
||||
maxsize = def.maxsize or 2.4,
|
||||
texture = "mcl_particles_bonemeal.png^[colorize:#00EE00:125", -- TODO: real MC color
|
||||
glow = def.glow or 1,
|
||||
})
|
||||
mcl_bone_meal.add_bone_meal_particle(pos, def)
|
||||
end
|
||||
|
||||
mcl_dye.bone_meal_callbacks = {}
|
||||
|
||||
function mcl_dye.register_on_bone_meal_apply(func)
|
||||
table.insert(mcl_dye.bone_meal_callbacks, func)
|
||||
minetest.log("warning", "mcl_dye.register_on_bone_meal_apply() is deprecated. Read mcl_bone_meal/API.md!")
|
||||
mcl_bone_meal.register_on_bone_meal_apply(func)
|
||||
end
|
||||
|
||||
local function apply_bone_meal(pointed_thing, user)
|
||||
-- Bone meal currently spawns all flowers found in the plains.
|
||||
local flowers_table_plains = {
|
||||
"mcl_flowers:dandelion",
|
||||
"mcl_flowers:dandelion",
|
||||
"mcl_flowers:poppy",
|
||||
|
||||
"mcl_flowers:oxeye_daisy",
|
||||
"mcl_flowers:tulip_orange",
|
||||
"mcl_flowers:tulip_red",
|
||||
"mcl_flowers:tulip_white",
|
||||
"mcl_flowers:tulip_pink",
|
||||
"mcl_flowers:azure_bluet",
|
||||
}
|
||||
local flowers_table_simple = {
|
||||
"mcl_flowers:dandelion",
|
||||
"mcl_flowers:poppy",
|
||||
}
|
||||
local flowers_table_swampland = {
|
||||
"mcl_flowers:blue_orchid",
|
||||
}
|
||||
local flowers_table_flower_forest = {
|
||||
"mcl_flowers:dandelion",
|
||||
"mcl_flowers:poppy",
|
||||
"mcl_flowers:oxeye_daisy",
|
||||
"mcl_flowers:tulip_orange",
|
||||
"mcl_flowers:tulip_red",
|
||||
"mcl_flowers:tulip_white",
|
||||
"mcl_flowers:tulip_pink",
|
||||
"mcl_flowers:azure_bluet",
|
||||
"mcl_flowers:allium",
|
||||
}
|
||||
|
||||
local pos = pointed_thing.under
|
||||
local n = minetest.get_node(pos)
|
||||
if n.name == "" then return false end
|
||||
|
||||
if mcl_util.check_area_protection(pos, pointed_thing.above, user) then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, func in pairs(mcl_dye.bone_meal_callbacks) do
|
||||
if func(pointed_thing, user) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.get_item_group(n.name, "sapling") >= 1 then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Saplings: 45% chance to advance growth stage
|
||||
if math.random(1, 100) <= 45 then
|
||||
if n.name == "mcl_cherry_blossom:cherrysapling" then
|
||||
return mcl_cherry_blossom.generate_cherry_tree(pos) -- If cherry blossom sapling, run that callback instead.
|
||||
else
|
||||
return mcl_core.grow_sapling(pos, n)
|
||||
end
|
||||
end
|
||||
elseif minetest.get_item_group(n.name, "mushroom") == 1 then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Try to grow huge mushroom
|
||||
|
||||
-- Must be on a dirt-type block
|
||||
local below = minetest.get_node({x = pos.x, y = pos.y - 1, z = pos.z})
|
||||
if below.name ~= "mcl_core:mycelium" and below.name ~= "mcl_core:dirt" and minetest.get_item_group(below.name, "grass_block") ~= 1 and below.name ~= "mcl_core:coarse_dirt" and below.name ~= "mcl_core:podzol" then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Select schematic
|
||||
local schematic, offset, height
|
||||
if n.name == "mcl_mushrooms:mushroom_brown" then
|
||||
schematic = minetest.get_modpath("mcl_mushrooms").."/schematics/mcl_mushrooms_huge_brown.mts"
|
||||
offset = { x = -3, y = -1, z = -3 }
|
||||
height = 8
|
||||
elseif n.name == "mcl_mushrooms:mushroom_red" then
|
||||
schematic = minetest.get_modpath("mcl_mushrooms").."/schematics/mcl_mushrooms_huge_red.mts"
|
||||
offset = { x = -2, y = -1, z = -2 }
|
||||
height = 8
|
||||
else
|
||||
return false
|
||||
end
|
||||
-- 40% chance
|
||||
if math.random(1, 100) <= 40 then
|
||||
-- Check space requirements
|
||||
for i=1,3 do
|
||||
local cpos = vector.add(pos, {x=0, y=i, z=0})
|
||||
if minetest.get_node(cpos).name ~= "air" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local yoff = 3
|
||||
local minp, maxp = {x=pos.x-3, y=pos.y+yoff, z=pos.z-3}, {x=pos.x+3, y=pos.y+yoff+(height-3), z=pos.z+3}
|
||||
local diff = vector.subtract(maxp, minp)
|
||||
diff = vector.add(diff, {x=1,y=1,z=1})
|
||||
local totalnodes = diff.x * diff.y * diff.z
|
||||
local goodnodes = minetest.find_nodes_in_area(minp, maxp, {"air", "group:leaves"})
|
||||
if #goodnodes < totalnodes then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Place the huge mushroom
|
||||
minetest.remove_node(pos)
|
||||
local place_pos = vector.add(pos, offset)
|
||||
local ok = minetest.place_schematic(place_pos, schematic, 0, nil, false)
|
||||
return ok ~= nil
|
||||
end
|
||||
return false
|
||||
-- Wheat, Potato, Carrot, Pumpkin Stem, Melon Stem: Advance by 2-5 stages
|
||||
elseif string.find(n.name, "mcl_farming:wheat_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
local stages = math.random(2, 5)
|
||||
return mcl_farming:grow_plant("plant_wheat", pos, n, stages, true)
|
||||
elseif string.find(n.name, "mcl_farming:potato_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
local stages = math.random(2, 5)
|
||||
return mcl_farming:grow_plant("plant_potato", pos, n, stages, true)
|
||||
elseif string.find(n.name, "mcl_farming:carrot_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
local stages = math.random(2, 5)
|
||||
return mcl_farming:grow_plant("plant_carrot", pos, n, stages, true)
|
||||
elseif string.find(n.name, "mcl_farming:pumpkin_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
local stages = math.random(2, 5)
|
||||
return mcl_farming:grow_plant("plant_pumpkin_stem", pos, n, stages, true)
|
||||
elseif string.find(n.name, "mcl_farming:melontige_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
local stages = math.random(2, 5)
|
||||
return mcl_farming:grow_plant("plant_melon_stem", pos, n, stages, true)
|
||||
elseif string.find(n.name, "mcl_farming:beetroot_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Beetroot: 75% chance to advance to next stage
|
||||
if math.random(1, 100) <= 75 then
|
||||
return mcl_farming:grow_plant("plant_beetroot", pos, n, 1, true)
|
||||
end
|
||||
elseif string.find(n.name, "mcl_farming:sweet_berry_bush_") then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
if n.name == "mcl_farming:sweet_berry_bush_3" then
|
||||
return minetest.add_item(vector.offset(pos,math.random()-0.5,math.random()-0.5,math.random()-0.5),"mcl_farming:sweet_berry")
|
||||
else
|
||||
return mcl_farming:grow_plant("plant_sweet_berry_bush", pos, n, 1, true)
|
||||
end
|
||||
elseif n.name == "mcl_cocoas:cocoa_1" or n.name == "mcl_cocoas:cocoa_2" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Cocoa: Advance by 1 stage
|
||||
mcl_cocoas.grow(pos)
|
||||
return true
|
||||
elseif minetest.get_item_group(n.name, "grass_block") == 1 then
|
||||
-- Grass Block: Generate tall grass and random flowers all over the place
|
||||
for i = -7, 7 do
|
||||
for j = -7, 7 do
|
||||
for y = -1, 1 do
|
||||
pos = vector.offset(pointed_thing.above, i, y, j)
|
||||
n = minetest.get_node(pos)
|
||||
local n2 = minetest.get_node(vector.offset(pos, 0, -1, 0))
|
||||
|
||||
if n.name ~= "" and n.name == "air" and (minetest.get_item_group(n2.name, "grass_block_no_snow") == 1) then
|
||||
-- Randomly generate flowers, tall grass or nothing
|
||||
if math.random(1, 100) <= 90 / ((math.abs(i) + math.abs(j)) / 2)then
|
||||
-- 90% tall grass, 10% flower
|
||||
mcl_dye.add_bone_meal_particle(pos, {amount = 4})
|
||||
if math.random(1,100) <= 90 then
|
||||
local col = n2.param2
|
||||
minetest.add_node(pos, {name="mcl_flowers:tallgrass", param2=col})
|
||||
else
|
||||
local flowers_table
|
||||
if mg_name == "v6" then
|
||||
flowers_table = flowers_table_plains
|
||||
else
|
||||
local biome = minetest.get_biome_name(minetest.get_biome_data(pos).biome)
|
||||
if biome == "Swampland" or biome == "Swampland_shore" or biome == "Swampland_ocean" or biome == "Swampland_deep_ocean" or biome == "Swampland_underground" then
|
||||
flowers_table = flowers_table_swampland
|
||||
elseif biome == "FlowerForest" or biome == "FlowerForest_beach" or biome == "FlowerForest_ocean" or biome == "FlowerForest_deep_ocean" or biome == "FlowerForest_underground" then
|
||||
flowers_table = flowers_table_flower_forest
|
||||
elseif biome == "Plains" or biome == "Plains_beach" or biome == "Plains_ocean" or biome == "Plains_deep_ocean" or biome == "Plains_underground" or biome == "SunflowerPlains" or biome == "SunflowerPlains_ocean" or biome == "SunflowerPlains_deep_ocean" or biome == "SunflowerPlains_underground" then
|
||||
flowers_table = flowers_table_plains
|
||||
else
|
||||
flowers_table = flowers_table_simple
|
||||
end
|
||||
end
|
||||
minetest.add_node(pos, {name=flowers_table[math.random(1, #flowers_table)]})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
|
||||
-- Double flowers: Drop corresponding item
|
||||
elseif n.name == "mcl_flowers:rose_bush" or n.name == "mcl_flowers:rose_bush_top" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
minetest.add_item(pos, "mcl_flowers:rose_bush")
|
||||
return true
|
||||
elseif n.name == "mcl_flowers:peony" or n.name == "mcl_flowers:peony_top" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
minetest.add_item(pos, "mcl_flowers:peony")
|
||||
return true
|
||||
elseif n.name == "mcl_flowers:lilac" or n.name == "mcl_flowers:lilac_top" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
minetest.add_item(pos, "mcl_flowers:lilac")
|
||||
return true
|
||||
elseif n.name == "mcl_flowers:sunflower" or n.name == "mcl_flowers:sunflower_top" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
minetest.add_item(pos, "mcl_flowers:sunflower")
|
||||
return true
|
||||
|
||||
elseif n.name == "mcl_flowers:tallgrass" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Tall Grass: Grow into double tallgrass
|
||||
local toppos = { x=pos.x, y=pos.y+1, z=pos.z }
|
||||
local topnode = minetest.get_node(toppos)
|
||||
if minetest.registered_nodes[topnode.name].buildable_to then
|
||||
minetest.set_node(pos, { name = "mcl_flowers:double_grass", param2 = n.param2 })
|
||||
minetest.set_node(toppos, { name = "mcl_flowers:double_grass_top", param2 = n.param2 })
|
||||
return true
|
||||
end
|
||||
|
||||
--[[
|
||||
Here for when Bonemeal becomes an api, there's code if needed for handling applying to bamboo.
|
||||
-- Handle applying bonemeal to bamboo.
|
||||
elseif mcl_bamboo.is_bamboo(n.name) then
|
||||
local success = mcl_bamboo.grow_bamboo(pos, true)
|
||||
if success then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
end
|
||||
return success
|
||||
--]]
|
||||
elseif n.name == "mcl_flowers:fern" then
|
||||
mcl_dye.add_bone_meal_particle(pos)
|
||||
-- Fern: Grow into large fern
|
||||
local toppos = { x=pos.x, y=pos.y+1, z=pos.z }
|
||||
local topnode = minetest.get_node(toppos)
|
||||
if minetest.registered_nodes[topnode.name].buildable_to then
|
||||
minetest.set_node(pos, { name = "mcl_flowers:double_fern", param2 = n.param2 })
|
||||
minetest.set_node(toppos, { name = "mcl_flowers:double_fern_top", param2 = n.param2 })
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
mcl_dye.apply_bone_meal = apply_bone_meal
|
||||
|
||||
-- Bone meal item registration.
|
||||
--
|
||||
-- To be moved into its own mod.
|
||||
--
|
||||
minetest.register_craftitem(":mcl_bone_meal:bone_meal", {
|
||||
inventory_image = "mcl_bone_meal_bone_meal.png",
|
||||
description = S("Bone Meal"),
|
||||
_tt_help = S("Speeds up plant growth"),
|
||||
_doc_items_longdesc = S("Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants."),
|
||||
_doc_items_usagehelp = S("Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place."),
|
||||
stack_max = 64,
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
-- Use pointed node's on_rightclick function first, if present
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
if user and not user:get_player_control().sneak then
|
||||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, user, itemstack) or itemstack
|
||||
end
|
||||
end
|
||||
|
||||
-- Use the bone meal on the ground
|
||||
if (apply_bone_meal(pointed_thing, user) and (not minetest.is_creative_enabled(user:get_player_name()))) then
|
||||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
_on_dispense = function(stack, pos, droppos, dropnode, dropdir)
|
||||
-- Apply bone meal, if possible
|
||||
local pointed_thing
|
||||
if dropnode.name == "air" then
|
||||
pointed_thing = { above = droppos, under = { x=droppos.x, y=droppos.y-1, z=droppos.z } }
|
||||
else
|
||||
pointed_thing = { above = pos, under = droppos }
|
||||
end
|
||||
local success = apply_bone_meal(pointed_thing, nil)
|
||||
if success then
|
||||
stack:take_item()
|
||||
end
|
||||
return stack
|
||||
end,
|
||||
_dispense_into_walkable = true
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "mcl_bone_meal:bone_meal 3",
|
||||
recipe = {{"mcl_mobitems:bone"}},
|
||||
})
|
||||
|
||||
|
||||
-- Dye creation recipes.
|
||||
--
|
||||
minetest.register_craft({
|
||||
|
@ -609,19 +303,16 @@ minetest.register_craft({
|
|||
output = "mcl_dye:magenta 2",
|
||||
recipe = {"mcl_dye:violet", "mcl_dye:pink"},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mcl_dye:pink 2",
|
||||
recipe = {"mcl_dye:red", "mcl_dye:white"},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mcl_dye:cyan 2",
|
||||
recipe = {"mcl_dye:blue", "mcl_dye:dark_green"},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "mcl_dye:violet 2",
|
||||
|
|
|
@ -17,11 +17,3 @@ Magenta Dye=Magenta Farbstoff
|
|||
Pink Dye=Rosa Farbstoff
|
||||
This item is a dye which is used for dyeing and crafting.=Dieser Gegenstand ist ein Farbstoff, der zum Einfärben und in der Herstellung benutzt werden kann.
|
||||
Rightclick on a sheep to dye its wool. Other things are dyed by crafting.=Rechtsklicken Sie auf ein Schaf, um seine Wolle zu färben. Andere Dinge werden mit der Fertigung eingefärbt.
|
||||
Bone Meal=Knochenmehl
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Knochenmehl ist ein weißer Farbstoff und auch nützlich als Dünger, um das Wachstum vieler Pflanzen zu beschleunigen.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Rechtsklicken Sie auf ein Schaf, um die Wolle weiß einzufärben. Rechtsklicken Sie auf eine Pflanze, um ihr Wachstum zu beschleunigen. Beachten Sie, dass nicht alle Pflanzen darauf ansprechen. Benutzen Sie es auf einem Grasblock, wächst viel hohes Gras und vielleicht auch ein paar Blumen.
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=Kakaobohnen sind ein brauner Farbstoff und werden benutzt, um Kakao anzupflanzen.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Rechtsklicken Sie auf ein Schaf, um die Wolle braun einzufärben. Rechtsklicken Sie an die Seite eines Dschungelbaumstamms (Dschungelholz), um eine junge Kakaoschote zu pflanzen.
|
||||
Cocoa Beans=Kakaobohnen
|
||||
Grows at the side of jungle trees=Wächst an der Seite von Dschungelbäumen
|
||||
Speeds up plant growth=Beschleunigt Pflanzenwachstum
|
||||
|
|
|
@ -17,9 +17,3 @@ Magenta Dye=Tinte magenta
|
|||
Pink Dye=Tinte rosado
|
||||
This item is a dye which is used for dyeing and crafting.=Este artículo es un tinte que se utiliza para teñir y elaborar.
|
||||
Rightclick on a sheep to dye its wool. Other things are dyed by crafting.=Haga clic derecho sobre una oveja para teñir su lana. Otras cosas pueden ser teñidas mediante la elaboración.
|
||||
Bone Meal=Harina de hueso
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La harina de hueso es un tinte blanco y también es útil como fertilizante para acelerar el crecimiento de muchas plantas.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=RHaga clic derecho en una oveja para volver su lana blanca. Haga clic derecho en una planta para acelerar su crecimiento. Tenga en cuenta que no todas las plantas pueden ser fertilizadas de esta manera. Cuando haces clic derecho en un bloque de hierba, crecerán hierba alta y flores por todo el lugar.
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=Los granos de cacao son un tinte marrón y se pueden usar para plantar cacao.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Haga clic derecho en una oveja para convertir su lana en marrón. Haga clic derecho en el costado del tronco de un árbol de jungla para plantar un cacao joven.
|
||||
Cocoa Beans=Granos de cacao
|
||||
|
|
|
@ -17,11 +17,3 @@ Magenta Dye=Teinture Magenta
|
|||
Pink Dye=Teinture Rose
|
||||
This item is a dye which is used for dyeing and crafting.=Cet objet est un colorant utilisé pour la teinture et l'artisanat.
|
||||
Rightclick on a sheep to dye its wool. Other things are dyed by crafting.=Clic droit sur un mouton pour teindre sa laine. D'autres choses sont teintes par l'artisanat.
|
||||
Bone Meal=Farine d'Os
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=La farine d'os est une teinture blanche et également utile comme engrais pour accélérer la croissance de nombreuses plantes.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Cliquez avec le bouton droit sur un mouton pour blanchir sa laine. Cliquez avec le bouton droit sur une plante pour accélérer sa croissance. Notez que toutes les plantes ne peuvent pas être fertilisées comme ça. Lorsque vous cliquez avec le bouton droit sur un bloc d'herbe, les hautes herbes et les fleurs poussent partout.
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=Les fèves de cacao ont une teinture brune et peuvent être utilisées pour planter du cacao.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Faites un clic droit sur un mouton pour brunir sa laine. Clic droit sur le côté d'un tronc d'arbre de la jungle (Bois Acajou) pour planter un jeune cacao.
|
||||
Cocoa Beans=Fèves de Cacao
|
||||
Grows at the side of jungle trees=Pousse à côté des arbres de la jungle
|
||||
Speeds up plant growth=Accélère la croissance des plantes
|
||||
|
|
|
@ -17,11 +17,3 @@ Magenta Dye=Karmazynowa farba
|
|||
Pink Dye=Różowa farba
|
||||
This item is a dye which is used for dyeing and crafting.=Ten przedmiot to farba wykorzystywana to farbowania i wytwarzania.
|
||||
Rightclick on a sheep to dye its wool. Other things are dyed by crafting.=Kliknij prawym na owcę aby zafarbować jej wełnę. Inne rzeczy mogą być zafarbowane przy wytwarzaniu.
|
||||
Bone Meal=Mączka kostna
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=Mączka kostna to biała farba i przydatny nawóz, który przyspiesza rośnięcie wielu roślin.
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Kliknij prawym na owcę, aby wybielić jej wełnę. Kliknij prawym na roślinę aby przyspieszyć jej wzrost. Zważ, że nie na wszystkie rośliny to tak działa. Gdy klikniesz prawym na blok trawy, wysoka trawa wyrośnie wokół.
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=Ziarna kakaowe mogą być wykorzystane do sadzenia kakao.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Naciśnij prawym aby zafarbować wełnę owcy na brązowo. Naciśnij prawym na boku tropikalnego pnia (Tropikalne drewno) aby zasadzić młode kakao.
|
||||
Cocoa Beans=Ziarna kakaowe
|
||||
Grows at the side of jungle trees=Rośnie na boku tropikalnych drzew
|
||||
Speeds up plant growth=Przyspiesza wzrost roślin
|
||||
|
|
|
@ -22,6 +22,3 @@ Bone meal is a white dye and also useful as a fertilizer to speed up the growth
|
|||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=Кликните правой по овце, чтобы сделать её шерсть белой. Кликните правой по растению, чтобы ускорить его рост. Имейте в виду, что не все растения можно удобрять таким способом. Если вы кликнете по травяному блоку, то на этом месте вырастет высокая трава и цветы.
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=Какао-бобы являются коричневым красителем. Их также можно использовать, чтобы посадить какао.
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=Кликните правой по овце, чтобы сделать её шерсть коричневой. Кликните правой по боковой части ствола тропического дерева, чтобы посадить молодое какао.
|
||||
Cocoa Beans=Какао-бобы
|
||||
Grows at the side of jungle trees=Растут на стволах тропических деревьев
|
||||
Speeds up plant growth=Ускоряет рост растений
|
||||
|
|
|
@ -17,9 +17,3 @@ Magenta Dye=洋紅色染料
|
|||
Pink Dye=粉紅色染料
|
||||
This item is a dye which is used for dyeing and crafting.=這個物品是一種用於染色和合成的染料。
|
||||
Rightclick on a sheep to dye its wool. Other things are dyed by crafting.=右鍵單擊綿羊以染它的毛。其他東西是通過合成染色的。
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=骨粉是一種白色染料,也可作為肥料,加速許多植物的生長。
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=右鍵點擊一隻羊,使其羊毛變白。右鍵點擊一株植物以加快其生長速度。注意,不是所有的植物都能像這樣施肥。當你右鍵點擊一個草方時,高高的草和花會到處生長。
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=可可豆是一種棕色染料,也可用於種植可可。
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=右鍵點擊一隻羊,使其羊毛變成褐色。右鍵點擊叢林木的一側,可以種植一個可可。
|
||||
Grows at the side of jungle trees=在叢林木側生長
|
||||
Speeds up plant growth=加速植物生長
|
||||
|
|
|
@ -17,11 +17,3 @@ Magenta Dye=
|
|||
Pink Dye=
|
||||
This item is a dye which is used for dyeing and crafting.=
|
||||
Rightclick on a sheep to dye its wool. Other things are dyed by crafting.=
|
||||
Bone Meal=
|
||||
Bone meal is a white dye and also useful as a fertilizer to speed up the growth of many plants.=
|
||||
Rightclick a sheep to turn its wool white. Rightclick a plant to speed up its growth. Note that not all plants can be fertilized like this. When you rightclick a grass block, tall grass and flowers will grow all over the place.=
|
||||
Cocoa beans are a brown dye and can be used to plant cocoas.=
|
||||
Rightclick a sheep to turn its wool brown. Rightclick on the side of a jungle tree trunk (Jungle Wood) to plant a young cocoa.=
|
||||
Cocoa Beans=
|
||||
Grows at the side of jungle trees=
|
||||
Speeds up plant growth=
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
name = mcl_dye
|
||||
depends = mcl_core, mcl_flowers, mcl_mobitems, mcl_cocoas
|
||||
description = Adds color to your world!
|
||||
depends = mcl_bone_meal, mcl_cocoas
|
||||
|
|
|
@ -13,78 +13,44 @@ minetest.register_craftitem("mcl_farming:beetroot_seeds", {
|
|||
end
|
||||
})
|
||||
|
||||
minetest.register_node("mcl_farming:beetroot_0", {
|
||||
description = S("Premature Beetroot Plant (Stage 1)"),
|
||||
_doc_items_longdesc = S("Beetroot plants are plants which grow on farmland under sunlight in 4 stages. On hydrated farmland, they grow a bit faster. They can be harvested at any time but will only yield a profit when mature."),
|
||||
_doc_items_entry_name = S("Premature Beetroot Plant"),
|
||||
paramtype = "light",
|
||||
paramtype2 = "meshoptions",
|
||||
sunlight_propagates = true,
|
||||
place_param2 = 3,
|
||||
walkable = false,
|
||||
drawtype = "plantlike",
|
||||
drop = "mcl_farming:beetroot_seeds",
|
||||
tiles = {"mcl_farming_beetroot_0.png"},
|
||||
inventory_image = "mcl_farming_beetroot_0.png",
|
||||
wield_image = "mcl_farming_beetroot_0.png",
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, -5/16, 0.5}
|
||||
},
|
||||
},
|
||||
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
local size = {[0]=-5, -4, -3}
|
||||
|
||||
minetest.register_node("mcl_farming:beetroot_1", {
|
||||
description = S("Premature Beetroot Plant (Stage 2)"),
|
||||
_doc_items_create_entry = false,
|
||||
paramtype = "light",
|
||||
paramtype2 = "meshoptions",
|
||||
sunlight_propagates = true,
|
||||
place_param2 = 3,
|
||||
walkable = false,
|
||||
drawtype = "plantlike",
|
||||
drop = "mcl_farming:beetroot_seeds",
|
||||
tiles = {"mcl_farming_beetroot_1.png"},
|
||||
inventory_image = "mcl_farming_beetroot_1.png",
|
||||
wield_image = "mcl_farming_beetroot_1.png",
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, -4/16, 0.5}
|
||||
for i = 0, 2 do
|
||||
minetest.register_node("mcl_farming:beetroot_".. i, {
|
||||
description = S("Premature Beetroot Plant (Stage ".. i + 1 ..")"),
|
||||
_doc_items_longdesc = S("Beetroot plants are plants which grow on farmland under sunlight in 4 stages. On hydrated farmland, they grow a bit faster. They can be harvested at any time but will only yield a profit when mature."),
|
||||
_doc_items_entry_name = S("Premature Beetroot Plant"),
|
||||
paramtype = "light",
|
||||
paramtype2 = "meshoptions",
|
||||
sunlight_propagates = true,
|
||||
place_param2 = 3,
|
||||
walkable = false,
|
||||
drawtype = "plantlike",
|
||||
drop = "mcl_farming:beetroot_seeds",
|
||||
tiles = {"mcl_farming_beetroot_".. i ..".png"},
|
||||
inventory_image = "mcl_farming_beetroot_".. i ..".png",
|
||||
wield_image = "mcl_farming_beetroot_".. i ..".png",
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { {-0.5, -0.5, -0.5, 0.5, size[i]/16, 0.5} },
|
||||
},
|
||||
},
|
||||
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
|
||||
minetest.register_node("mcl_farming:beetroot_2", {
|
||||
description = S("Premature Beetroot Plant (Stage 3)"),
|
||||
_doc_items_create_entry = false,
|
||||
paramtype = "light",
|
||||
paramtype2 = "meshoptions",
|
||||
sunlight_propagates = true,
|
||||
place_param2 = 3,
|
||||
walkable = false,
|
||||
drawtype = "plantlike",
|
||||
drop = "mcl_farming:beetroot_seeds",
|
||||
tiles = {"mcl_farming_beetroot_2.png"},
|
||||
inventory_image = "mcl_farming_beetroot_2.png",
|
||||
wield_image = "mcl_farming_beetroot_2.png",
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, -3/16, 0.5}
|
||||
groups = {
|
||||
dig_immediate=3, not_in_creative_inventory=1,
|
||||
plant=1, attached_node=1, dig_by_water=1,
|
||||
destroy_by_lava_flow=1,dig_by_piston=1
|
||||
},
|
||||
},
|
||||
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
})
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local n = minetest.get_node(pos)
|
||||
-- 75% chance to advance to next stage
|
||||
if math.random(1, 100) <= 75 then
|
||||
return mcl_farming:grow_plant("plant_beetroot", pos, n, 1, true)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_node("mcl_farming:beetroot", {
|
||||
description = S("Mature Beetroot Plant"),
|
||||
|
@ -171,7 +137,8 @@ minetest.register_craft({
|
|||
},
|
||||
})
|
||||
|
||||
mcl_farming:add_plant("plant_beetroot", "mcl_farming:beetroot", {"mcl_farming:beetroot_0", "mcl_farming:beetroot_1", "mcl_farming:beetroot_2"}, 68, 3)
|
||||
-- beetroots grow at 2/3rd of the default speed
|
||||
mcl_farming:add_plant("plant_beetroot", "mcl_farming:beetroot", {"mcl_farming:beetroot_0", "mcl_farming:beetroot_1", "mcl_farming:beetroot_2"}, 8.7012, 35)
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
for i = 1, 2 do
|
||||
|
|
|
@ -45,6 +45,12 @@ for i=1, 7 do
|
|||
groups = {dig_immediate=3, not_in_creative_inventory=1,plant=1,attached_node=1,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1},
|
||||
sounds = mcl_sounds.node_sound_leaves_defaults(),
|
||||
_mcl_blast_resistance = 0,
|
||||
_on_bone_meal = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under
|
||||
local n = minetest.get_node(pos)
|
||||
local stages = math.random(2, 5)
|
||||
return mcl_farming:grow_plant("plant_carrot", pos, n, stages, true)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -118,7 +124,7 @@ minetest.register_craft({
|
|||
}
|
||||
})
|
||||
|
||||
mcl_farming:add_plant("plant_carrot", "mcl_farming:carrot", {"mcl_farming:carrot_1", "mcl_farming:carrot_2", "mcl_farming:carrot_3", "mcl_farming:carrot_4", "mcl_farming:carrot_5", "mcl_farming:carrot_6", "mcl_farming:carrot_7"}, 25, 20)
|
||||
mcl_farming:add_plant("plant_carrot", "mcl_farming:carrot", {"mcl_farming:carrot_1", "mcl_farming:carrot_2", "mcl_farming:carrot_3", "mcl_farming:carrot_4", "mcl_farming:carrot_5", "mcl_farming:carrot_6", "mcl_farming:carrot_7"}, 5.8013, 35)
|
||||
|
||||
if minetest.get_modpath("doc") then
|
||||
for i=2,7 do
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue