Leafdecay: Node timer based implementation, API

This implements a node-timer based leafdecay mechanism, and exposes
an API to use it in mods.

The API is documented in game_api.txt.

`default.register_leafdecay(leafdecaydef)`

`leafdecaydef` is a table, with following members:
{
	trunks = { "default:tree"}, -- nodes considered trunks
	leaves = { "default:leaves", "default:apple"}, -- nodes considered leaves
	radius = 3, -- activates leafdecay this far from the trunk
}

The algorithm will drop `leaves` items in the area if no `trunks` are found
in the `trunk_radius` sized area around the position of the leaf. If a node
listed in `leaves` has a group `leafdecay_drop > 0`, then the item is dropped,
otherwise the item is removed but not dropped.

The algorithm also implements a value `default.leafdecay_speed` (default
15) which can be modified to increase or decrease of the leaf decay. The
algorithm will vary the actual speed a bit to introduce randomness.

Leaf decay is randomized by 0.1 seconds to reduce the chance that
decay happens many times on the same second interval. This requires
nodetimer_interval to be set to values lower than 1.0 to have an
effect.

The leaves will decay between 2 and 10 seconds after digging the trunk,
and happen at non-integer second intervals.

-- The API was added by sofar.
This commit is contained in:
MarkuBu 2017-02-07 16:28:02 -08:00 committed by paramat
parent 37dd910747
commit 60e5b299af
3 changed files with 132 additions and 45 deletions

View File

@ -658,20 +658,36 @@ default.player_get_animation(player)
Leafdecay Leafdecay
--------- ---------
To enable leaf decay for a node, add it to the `leafdecay` group. To enable leaf decay for leaves when a tree is cut down by a player,
register the tree with the default.register_leafdecay(leafdecaydef)
function.
The rating of the group determines how far from a node in the group `tree` If `param2` of any registered node is ~= 0, the node will always be
the node can be without decaying. preserved. Thus, if the player places a node of that kind, you will
want to set `param2 = 1` or so.
If `param2` of the node is ~= 0, the node will always be preserved. Thus, if The function `default.after_place_leaves` can be set as
the player places a node of that kind, you will want to set `param2 = 1` or so. `after_place_node of a node` to set param2 to 1 if the player places
the node (should not be used for nodes that use param2 otherwise
(e.g. facedir)).
The function `default.after_place_leaves` can be set as `after_place_node of a node` If the node is in the `leafdecay_drop` group then it will always be
to set param2 to 1 if the player places the node (should not be used for nodes dropped as an item.
that use param2 otherwise (e.g. facedir)).
`default.register_leafdecay(leafdecaydef)`
`leafdecaydef` is a table, with following members:
{
trunks = {"default:tree"}, -- nodes considered trunks
leaves = {"default:leaves", "default:apple"},
-- nodes considered for removal
radius = 3, -- radius to consider for searching
}
Note: all the listed nodes in `trunks` have their `on_after_destruct`
callback overridden. All the nodes listed in `leaves` have their
`on_timer` callback overridden.
If the node is in the `leafdecay_drop` group then it will always be dropped as an
item.
Dyes Dyes
---- ----

View File

@ -325,47 +325,64 @@ default.after_place_leaves = function(pos, placer, itemstack, pointed_thing)
end end
end end
-- Leafdecay ABM -- Leafdecay
local function leafdecay_after_destruct(pos, oldnode, def)
minetest.register_abm({ for _, v in pairs(minetest.find_nodes_in_area(vector.subtract(pos, def.radius),
label = "Leaf decay", vector.add(pos, def.radius), def.leaves)) do
nodenames = {"group:leafdecay"}, local node = minetest.get_node(v)
neighbors = {"air"}, if node.param2 == 0 then
interval = 2, minetest.get_node_timer(v):start(math.random(20, 120) / 10)
chance = 10, end
catch_up = false, end
action = function(pos, node, _, _)
-- Check if leaf is placed
if node.param2 ~= 0 then
return
end end
local rad = minetest.registered_nodes[node.name].groups.leafdecay local function leafdecay_on_timer(pos, def)
-- Assume ignore is a trunk, to make this if minetest.find_node_near(pos, def.radius, def.trunks) then
-- work at the border of a loaded area return false
if minetest.find_node_near(pos, rad, {"ignore", "group:tree"}) then
return
end end
-- Drop stuff
local itemstacks = minetest.get_node_drops(node.name) local node = minetest.get_node(pos)
for _, itemname in ipairs(itemstacks) do local drops = minetest.get_node_drops(node.name)
if itemname ~= node.name or for _, item in ipairs(drops) do
minetest.get_item_group(node.name, "leafdecay_drop") ~= 0 then local is_leaf
local p_drop = { for _, v in pairs(def.leaves) do
if v == item then
is_leaf = true
end
end
if minetest.get_item_group(item, "leafdecay_drop") ~= 0 or
not is_leaf then
minetest.add_item({
x = pos.x - 0.5 + math.random(), x = pos.x - 0.5 + math.random(),
y = pos.y - 0.5 + math.random(), y = pos.y - 0.5 + math.random(),
z = pos.z - 0.5 + math.random(), z = pos.z - 0.5 + math.random(),
} }, item)
minetest.add_item(p_drop, itemname)
end end
end end
-- Remove node
minetest.remove_node(pos) minetest.remove_node(pos)
minetest.check_for_falling(pos) minetest.check_for_falling(pos)
end end
})
function default.register_leafdecay(def)
assert(def.leaves)
assert(def.trunks)
assert(def.radius)
for _, v in pairs(def.trunks) do
minetest.override_item(v, {
after_destruct = function(pos, oldnode)
leafdecay_after_destruct(pos, oldnode, def)
end,
})
end
for _, v in pairs(def.leaves) do
minetest.override_item(v, {
on_timer = function(pos)
leafdecay_on_timer(pos, def)
end,
})
end
end
-- --
-- Convert dirt to something that fits the environment -- Convert dirt to something that fits the environment

View File

@ -2072,3 +2072,57 @@ minetest.register_node("default:cloud", {
sounds = default.node_sound_defaults(), sounds = default.node_sound_defaults(),
groups = {not_in_creative_inventory = 1}, groups = {not_in_creative_inventory = 1},
}) })
--
-- register trees for leafdecay
--
if minetest.get_mapgen_setting("mg_name") == "v6" then
default.register_leafdecay({
trunks = {"default:tree"},
leaves = {"default:apple", "default:leaves"},
radius = 2,
})
default.register_leafdecay({
trunks = {"default:jungletree"},
leaves = {"default:jungleleaves"},
radius = 3,
})
default.register_leafdecay({
trunks = {"default:pine_tree"},
leaves = {"default:pine_needles"},
radius = 3,
})
else
default.register_leafdecay({
trunks = {"default:tree"},
leaves = {"default:apple", "default:leaves"},
radius = 3,
})
default.register_leafdecay({
trunks = {"default:jungletree"},
leaves = {"default:jungleleaves"},
radius = 2,
})
default.register_leafdecay({
trunks = {"default:pine_tree"},
leaves = {"default:pine_needles"},
radius = 2,
})
end
default.register_leafdecay({
trunks = {"default:acacia_tree"},
leaves = {"default:acacia_leaves"},
radius = 2,
})
default.register_leafdecay({
trunks = {"default:aspen_tree"},
leaves = {"default:aspen_leaves"},
radius = 3,
})