As I began to grow and harvest an orchard of the extra trees I began to notice that sometimes a tree would grow near-instantaneously while others would languish for what seemed to be an indefinite time.
![]() |
The trees in the default game, however, seemed to grow at a steady rate. The growth asymmetry bothered me, so I decided to study and modify the code so that the mod would match the default game. Note, however, that I wrote the mod against the v0.4.16 version while v5.0.0 (v0.5.0 and company were skipped) is the latest at the time of this posting, so some information may be slightly dated.
minetest.register_abm({ nodenames = {"farming_plus:orange_sapling"}, interval = 60, chance = 20, action = function(pos, node) farming_plus.generate_tree(pos, "farming_plus:orange_tree", "farming_plus:orange_leaves", {"default:dirt", "default:dirt_with_grass"}, "farming_plus:orange", 20) end })nodenames are the nodes affected by the ABM, the amount of time that has to pass is the interval, the likelihood of growing is the chance, and the function for growing the tree is the action. The interval is measured in seconds, while the chance is actually an inverse value, that is, the chance is measured as 1 divided by the value of chance. For the mod's tree growth, the value of the interval was 60, meaning the tree had a chance to grow every minute, and the value of chance was 20, meaning there was a 1 in 20, or 5%, chance that it would grow; thus each tree had a 5% chance to grow every minute. So, when I'd harvest 20+ trees over a few minutes it was no wonder that a few would grow while I was still harvesting them.
minetest.register_node("farming_plus:orange_sapling", { ... on_timer = function(pos) farming_plus.generate_tree(pos, "farming_plus:orange_tree", "farming_plus:orange_leaves", {"default:dirt", "default:dirt_with_grass"}, "farming_plus:orange", 20) end, on_construct = function(pos) minetest.get_node_timer(pos):start(math.random(2400,4800)) end, })This meant that trees would then grow after 40 to 80 minutes (2400 to 4800 seconds, respectively), making growth generally much longer than the previous duration but also normalizing the growth rate. There's a caveat, though: suppose that the on_timer function is called but the tree doesn't grow because it's too dark out or some other reason. In that case the timer would be deactivated and the tree would never grow even if conditions changed; this meant that it was necessary to also reset the timer in the generate_tree function (again, using the values from the default trees):
if cant_grow then minetest.get_node_timer(pos):start(math.random(240, 600)) return endIn this case cant_grow is a bool which, obviously, has been set based on whether or not the tree can grow.
So far, both newly-created saplings and saplings with bad growth conditions are covered, but there's one more rather subtle case left uncovered: saplings which were created before the change to timer-based growth were made. It turns out that the timer definition actually applies to saplings retroactively, but the timer isn't activated and so they will never grow! In order to combat this I applied something called a Loading Block Modifier (LBM); what this does it that, each time an area with the specified nodes (in this case, saplings) is loaded, the specified action is applied to those nodes (in this case, causing the saplings to grow into their corresponding tree). Hence, I added the following code:
minetest.register_lbm({ name = "farming_plus:sapling_growth", nodenames = {"farming_plus:banana_sapling", "farming_plus:cocoa_sapling", "farming_plus:cherry_sapling", "farming_plus:orange_sapling"}, run_at_every_load = true, action = function(pos) local timer = minetest.get_node_timer(pos) if timer:is_started() == false then timer:start(math.random(2400, 4800)) end end })The nodenames field contains each node that will be affected by the LBM, the run_at_every_load field ensures that the LBM will actually be run for current blocks, and the action is the function to run on the loaded blocks. The important thing here is not to interfere with saplings whose growth timer has already been started. Thankfully, the timer provides an is_started method in order to check this; if the timer is not started, then, since older saplings will regardless have the growth timer attached to them, simply start the timer with the usual values. Interestingly enough, the game engine, as of this writing, works in such a way that: the timer is started, the time since the last load of the area is calculated, and then the time past is decremented from the timer. What this means is that a sapling may be triggered on the first load of an area if sufficient time has passed, rather than the first load simply triggering the timer and the corresponding wait. That's pretty satisfying, actually.
The values used in both the ABM and Node Timer methods predispose the ABM method to early growth, but what's important here is actually the shape of the two methods' rate. As you can see, the ABM method has a chance of ultra-early growth and a slight chance of ultra-late growth. The Node Timer method, on the other hand, has no chance of early or late growth. This will be the case for both growth methods regardless of the values used for growth time. As stated before, I prefer the latter due to its consistency.