Leafdecay: Node timer based implementation, API
authorMarkuBu <markus.burrer@gmail.com>
Wed, 8 Feb 2017 00:28:02 +0000 (16:28 -0800)
committerparamat <mat.gregory@virginmedia.com>
Sat, 25 Feb 2017 03:50:15 +0000 (03:50 +0000)
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.

game_api.txt
mods/default/functions.lua
mods/default/nodes.lua

index 0d518f1c884a70607a8f587bec78eff80aebfe12..fa6225a00baa3927dac21b9bb87b33fa788484e0 100644 (file)
@@ -658,20 +658,36 @@ default.player_get_animation(player)
 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`
-the node can be without decaying.
+If `param2` of any registered node is ~= 0, the node will always be
+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 player places a node of that kind, you will want to set `param2 = 1` or so.
+The function `default.after_place_leaves` can be set as
+`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`
-to set param2 to 1 if the player places the node (should not be used for nodes
-that use param2 otherwise (e.g. facedir)).
+If the node is in the `leafdecay_drop` group then it will always be
+dropped as an item.
+
+`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
 ----
index b9275209432cafc56712f85fce11dd7b25997d08..036110941b71bfe4579ff70beb519fb2b67ef6d5 100644 (file)
@@ -325,47 +325,64 @@ default.after_place_leaves = function(pos, placer, itemstack, pointed_thing)
        end
 end
 
--- Leafdecay ABM
-
-minetest.register_abm({
-       label = "Leaf decay",
-       nodenames = {"group:leafdecay"},
-       neighbors = {"air"},
-       interval = 2,
-       chance = 10,
-       catch_up = false,
-
-       action = function(pos, node, _, _)
-               -- Check if leaf is placed
-               if node.param2 ~= 0 then
-                       return
+-- Leafdecay
+local function leafdecay_after_destruct(pos, oldnode, def)
+       for _, v in pairs(minetest.find_nodes_in_area(vector.subtract(pos, def.radius),
+                       vector.add(pos, def.radius), def.leaves)) do
+               local node = minetest.get_node(v)
+               if node.param2 == 0 then
+                       minetest.get_node_timer(v):start(math.random(20, 120) / 10)
                end
+       end
+end
 
-               local rad = minetest.registered_nodes[node.name].groups.leafdecay
-               -- Assume ignore is a trunk, to make this
-               -- work at the border of a loaded area
-               if minetest.find_node_near(pos, rad, {"ignore", "group:tree"}) then
-                       return
-               end
-               -- Drop stuff
-               local itemstacks = minetest.get_node_drops(node.name)
-               for _, itemname in ipairs(itemstacks) do
-                       if itemname ~= node.name or
-                                       minetest.get_item_group(node.name, "leafdecay_drop") ~= 0 then
-                               local p_drop = {
-                                       x = pos.x - 0.5 + math.random(),
-                                       y = pos.y - 0.5 + math.random(),
-                                       z = pos.z - 0.5 + math.random(),
-                               }
-                               minetest.add_item(p_drop, itemname)
+local function leafdecay_on_timer(pos, def)
+       if minetest.find_node_near(pos, def.radius, def.trunks) then
+               return false
+       end
+
+       local node = minetest.get_node(pos)
+       local drops = minetest.get_node_drops(node.name)
+       for _, item in ipairs(drops) do
+               local is_leaf
+               for _, v in pairs(def.leaves) do
+                       if v == item then
+                               is_leaf = true
                        end
                end
-               -- Remove node
-               minetest.remove_node(pos)
-               minetest.check_for_falling(pos)
+               if minetest.get_item_group(item, "leafdecay_drop") ~= 0 or
+                               not is_leaf then
+                       minetest.add_item({
+                               x = pos.x - 0.5 + math.random(),
+                               y = pos.y - 0.5 + math.random(),
+                               z = pos.z - 0.5 + math.random(),
+                       }, item)
+               end
        end
-})
 
+       minetest.remove_node(pos)
+       minetest.check_for_falling(pos)
+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
index 0ce62a49e5fcf0d04f043dab7e90bd91b8f5d0b3..3d74b772e03b0903dc7b47bfdc47ddf6a10f3d8f 100644 (file)
@@ -2072,3 +2072,57 @@ minetest.register_node("default:cloud", {
        sounds = default.node_sound_defaults(),
        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,
+})