1 local random = math.random
4 -- Grow trees from saplings
9 function default.can_grow(pos)
10 local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
11 if not node_under then
14 local name_under = node_under.name
15 local is_soil = minetest.get_item_group(name_under, "soil")
19 local light_level = minetest.get_node_light(pos)
20 if not light_level or light_level < 13 then
27 -- 'is snow nearby' function
29 local function is_snow_nearby(pos)
30 return minetest.find_node_near(pos, 1, {"group:snowy"})
36 function default.grow_sapling(pos)
37 if not default.can_grow(pos) then
38 -- try again 5 min later
39 minetest.get_node_timer(pos):start(300)
43 local mg_name = minetest.get_mapgen_setting("mg_name")
44 local node = minetest.get_node(pos)
45 if node.name == "default:sapling" then
46 minetest.log("action", "A sapling grows into a tree at "..
47 minetest.pos_to_string(pos))
48 if mg_name == "v6" then
49 default.grow_tree(pos, random(1, 4) == 1)
51 default.grow_new_apple_tree(pos)
53 elseif node.name == "default:junglesapling" then
54 minetest.log("action", "A jungle sapling grows into a tree at "..
55 minetest.pos_to_string(pos))
56 if mg_name == "v6" then
57 default.grow_jungle_tree(pos)
59 default.grow_new_jungle_tree(pos)
61 elseif node.name == "default:pine_sapling" then
62 minetest.log("action", "A pine sapling grows into a tree at "..
63 minetest.pos_to_string(pos))
64 local snow = is_snow_nearby(pos)
65 if mg_name == "v6" then
66 default.grow_pine_tree(pos, snow)
68 default.grow_new_snowy_pine_tree(pos)
70 default.grow_new_pine_tree(pos)
72 elseif node.name == "default:acacia_sapling" then
73 minetest.log("action", "An acacia sapling grows into a tree at "..
74 minetest.pos_to_string(pos))
75 default.grow_new_acacia_tree(pos)
76 elseif node.name == "default:aspen_sapling" then
77 minetest.log("action", "An aspen sapling grows into a tree at "..
78 minetest.pos_to_string(pos))
79 default.grow_new_aspen_tree(pos)
80 elseif node.name == "default:bush_sapling" then
81 minetest.log("action", "A bush sapling grows into a bush at "..
82 minetest.pos_to_string(pos))
83 default.grow_bush(pos)
84 elseif node.name == "default:blueberry_bush_sapling" then
85 minetest.log("action", "A blueberry bush sapling grows into a bush at "..
86 minetest.pos_to_string(pos))
87 default.grow_blueberry_bush(pos)
88 elseif node.name == "default:acacia_bush_sapling" then
89 minetest.log("action", "An acacia bush sapling grows into a bush at "..
90 minetest.pos_to_string(pos))
91 default.grow_acacia_bush(pos)
92 elseif node.name == "default:pine_bush_sapling" then
93 minetest.log("action", "A pine bush sapling grows into a bush at "..
94 minetest.pos_to_string(pos))
95 default.grow_pine_bush(pos)
96 elseif node.name == "default:emergent_jungle_sapling" then
97 minetest.log("action", "An emergent jungle sapling grows into a tree at "..
98 minetest.pos_to_string(pos))
99 default.grow_new_emergent_jungle_tree(pos)
103 minetest.register_lbm({
104 name = "default:convert_saplings_to_node_timer",
105 nodenames = {"default:sapling", "default:junglesapling",
106 "default:pine_sapling", "default:acacia_sapling",
107 "default:aspen_sapling"},
108 action = function(pos)
109 minetest.get_node_timer(pos):start(math.random(300, 1500))
117 -- Apple tree and jungle tree trunk and leaves function
119 local function add_trunk_and_leaves(data, a, pos, tree_cid, leaves_cid,
120 height, size, iters, is_apple_tree)
121 local x, y, z = pos.x, pos.y, pos.z
122 local c_air = minetest.get_content_id("air")
123 local c_ignore = minetest.get_content_id("ignore")
124 local c_apple = minetest.get_content_id("default:apple")
127 data[a:index(x, y, z)] = tree_cid -- Force-place lowest trunk node to replace sapling
128 for yy = y + 1, y + height - 1 do
129 local vi = a:index(x, yy, z)
130 local node_id = data[vi]
131 if node_id == c_air or node_id == c_ignore or node_id == leaves_cid then
136 -- Force leaves near the trunk
137 for z_dist = -1, 1 do
138 for y_dist = -size, 1 do
139 local vi = a:index(x - 1, y + height + y_dist, z + z_dist)
140 for x_dist = -1, 1 do
141 if data[vi] == c_air or data[vi] == c_ignore then
142 if is_apple_tree and random(1, 8) == 1 then
145 data[vi] = leaves_cid
153 -- Randomly add leaves in 2x2x2 clusters.
155 local clust_x = x + random(-size, size - 1)
156 local clust_y = y + height + random(-size, 0)
157 local clust_z = z + random(-size, size - 1)
162 local vi = a:index(clust_x + xi, clust_y + yi, clust_z + zi)
163 if data[vi] == c_air or data[vi] == c_ignore then
164 if is_apple_tree and random(1, 8) == 1 then
167 data[vi] = leaves_cid
179 function default.grow_tree(pos, is_apple_tree, bad)
181 NOTE: Tree-placing code is currently duplicated in the engine
182 and in games that have saplings; both are deprecated but not
186 error("Deprecated use of default.grow_tree")
189 local x, y, z = pos.x, pos.y, pos.z
190 local height = random(4, 5)
191 local c_tree = minetest.get_content_id("default:tree")
192 local c_leaves = minetest.get_content_id("default:leaves")
194 local vm = minetest.get_voxel_manip()
195 local minp, maxp = vm:read_from_map(
196 {x = x - 2, y = y, z = z - 2},
197 {x = x + 2, y = y + height + 1, z = z + 2}
199 local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
200 local data = vm:get_data()
202 add_trunk_and_leaves(data, a, pos, c_tree, c_leaves, height, 2, 8, is_apple_tree)
212 function default.grow_jungle_tree(pos, bad)
214 NOTE: Jungletree-placing code is currently duplicated in the engine
215 and in games that have saplings; both are deprecated but not
219 error("Deprecated use of default.grow_jungle_tree")
222 local x, y, z = pos.x, pos.y, pos.z
223 local height = random(8, 12)
224 local c_air = minetest.get_content_id("air")
225 local c_ignore = minetest.get_content_id("ignore")
226 local c_jungletree = minetest.get_content_id("default:jungletree")
227 local c_jungleleaves = minetest.get_content_id("default:jungleleaves")
229 local vm = minetest.get_voxel_manip()
230 local minp, maxp = vm:read_from_map(
231 {x = x - 3, y = y - 1, z = z - 3},
232 {x = x + 3, y = y + height + 1, z = z + 3}
234 local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
235 local data = vm:get_data()
237 add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves,
238 height, 3, 30, false)
241 for z_dist = -1, 1 do
242 local vi_1 = a:index(x - 1, y - 1, z + z_dist)
243 local vi_2 = a:index(x - 1, y, z + z_dist)
244 for x_dist = -1, 1 do
245 if random(1, 3) >= 2 then
246 if data[vi_1] == c_air or data[vi_1] == c_ignore then
247 data[vi_1] = c_jungletree
248 elseif data[vi_2] == c_air or data[vi_2] == c_ignore then
249 data[vi_2] = c_jungletree
263 -- Pine tree from mg mapgen mod, design by sfan5, pointy top added by paramat
265 local function add_pine_needles(data, vi, c_air, c_ignore, c_snow, c_pine_needles)
266 local node_id = data[vi]
267 if node_id == c_air or node_id == c_ignore or node_id == c_snow then
268 data[vi] = c_pine_needles
272 local function add_snow(data, vi, c_air, c_ignore, c_snow)
273 local node_id = data[vi]
274 if node_id == c_air or node_id == c_ignore then
279 function default.grow_pine_tree(pos, snow)
280 local x, y, z = pos.x, pos.y, pos.z
281 local maxy = y + random(9, 13) -- Trunk top
283 local c_air = minetest.get_content_id("air")
284 local c_ignore = minetest.get_content_id("ignore")
285 local c_pine_tree = minetest.get_content_id("default:pine_tree")
286 local c_pine_needles = minetest.get_content_id("default:pine_needles")
287 local c_snow = minetest.get_content_id("default:snow")
289 local vm = minetest.get_voxel_manip()
290 local minp, maxp = vm:read_from_map(
291 {x = x - 3, y = y, z = z - 3},
292 {x = x + 3, y = maxy + 3, z = z + 3}
294 local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
295 local data = vm:get_data()
297 -- Upper branches layer
299 for yy = maxy - 1, maxy + 1 do
300 for zz = z - dev, z + dev do
301 local vi = a:index(x - dev, yy, zz)
302 local via = a:index(x - dev, yy + 1, zz)
303 for xx = x - dev, x + dev do
304 if random() < 0.95 - dev * 0.05 then
305 add_pine_needles(data, vi, c_air, c_ignore, c_snow,
308 add_snow(data, via, c_air, c_ignore, c_snow)
319 add_pine_needles(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow,
321 add_pine_needles(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow,
322 c_pine_needles) -- Paramat added a pointy top node
324 add_snow(data, a:index(x, maxy + 3, z), c_air, c_ignore, c_snow)
327 -- Lower branches layer
329 for i = 1, 20 do -- Random 2x2 squares of needles
330 local xi = x + random(-3, 2)
331 local yy = maxy + random(-6, -5)
332 local zi = z + random(-3, 2)
337 local vi = a:index(xi, yy, zz)
338 local via = a:index(xi, yy + 1, zz)
339 for xx = xi, xi + 1 do
340 add_pine_needles(data, vi, c_air, c_ignore, c_snow,
343 add_snow(data, via, c_air, c_ignore, c_snow)
352 for yy = my + 1, my + 2 do
353 for zz = z - dev, z + dev do
354 local vi = a:index(x - dev, yy, zz)
355 local via = a:index(x - dev, yy + 1, zz)
356 for xx = x - dev, x + dev do
357 if random() < 0.95 - dev * 0.05 then
358 add_pine_needles(data, vi, c_air, c_ignore, c_snow,
361 add_snow(data, via, c_air, c_ignore, c_snow)
372 -- Force-place lowest trunk node to replace sapling
373 data[a:index(x, y, z)] = c_pine_tree
374 for yy = y + 1, maxy do
375 local vi = a:index(x, yy, z)
376 local node_id = data[vi]
377 if node_id == c_air or node_id == c_ignore or
378 node_id == c_pine_needles or node_id == c_snow then
379 data[vi] = c_pine_tree
391 function default.grow_new_apple_tree(pos)
392 local path = minetest.get_modpath("default") ..
393 "/schematics/apple_tree_from_sapling.mts"
394 minetest.place_schematic({x = pos.x - 3, y = pos.y - 1, z = pos.z - 3},
395 path, "random", nil, false)
401 function default.grow_new_jungle_tree(pos)
402 local path = minetest.get_modpath("default") ..
403 "/schematics/jungle_tree_from_sapling.mts"
404 minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
405 path, "random", nil, false)
409 -- New emergent jungle tree
411 function default.grow_new_emergent_jungle_tree(pos)
412 local path = minetest.get_modpath("default") ..
413 "/schematics/emergent_jungle_tree_from_sapling.mts"
414 minetest.place_schematic({x = pos.x - 3, y = pos.y - 5, z = pos.z - 3},
415 path, "random", nil, false)
421 function default.grow_new_pine_tree(pos)
423 if math.random() > 0.5 then
424 path = minetest.get_modpath("default") ..
425 "/schematics/pine_tree_from_sapling.mts"
427 path = minetest.get_modpath("default") ..
428 "/schematics/small_pine_tree_from_sapling.mts"
430 minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
431 path, "0", nil, false)
435 -- New snowy pine tree
437 function default.grow_new_snowy_pine_tree(pos)
439 if math.random() > 0.5 then
440 path = minetest.get_modpath("default") ..
441 "/schematics/snowy_pine_tree_from_sapling.mts"
443 path = minetest.get_modpath("default") ..
444 "/schematics/snowy_small_pine_tree_from_sapling.mts"
446 minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
447 path, "random", nil, false)
453 function default.grow_new_acacia_tree(pos)
454 local path = minetest.get_modpath("default") ..
455 "/schematics/acacia_tree_from_sapling.mts"
456 minetest.place_schematic({x = pos.x - 4, y = pos.y - 1, z = pos.z - 4},
457 path, "random", nil, false)
463 function default.grow_new_aspen_tree(pos)
464 local path = minetest.get_modpath("default") ..
465 "/schematics/aspen_tree_from_sapling.mts"
466 minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
467 path, "0", nil, false)
471 -- Bushes do not need 'from sapling' schematic variants because
472 -- only the stem node is force-placed in the schematic.
476 function default.grow_bush(pos)
477 local path = minetest.get_modpath("default") ..
478 "/schematics/bush.mts"
479 minetest.place_schematic({x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
480 path, "0", nil, false)
485 function default.grow_blueberry_bush(pos)
486 local path = minetest.get_modpath("default") ..
487 "/schematics/blueberry_bush.mts"
488 minetest.place_schematic({x = pos.x - 1, y = pos.y, z = pos.z - 1},
489 path, "0", nil, false)
495 function default.grow_acacia_bush(pos)
496 local path = minetest.get_modpath("default") ..
497 "/schematics/acacia_bush.mts"
498 minetest.place_schematic({x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
499 path, "0", nil, false)
505 function default.grow_pine_bush(pos)
506 local path = minetest.get_modpath("default") ..
507 "/schematics/pine_bush.mts"
508 minetest.place_schematic({x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
509 path, "0", nil, false)
514 -- Sapling 'on place' function to check protection of node and resulting tree volume
517 function default.sapling_on_place(itemstack, placer, pointed_thing,
518 sapling_name, minp_relative, maxp_relative, interval)
519 -- Position of sapling
520 local pos = pointed_thing.under
521 local node = minetest.get_node_or_nil(pos)
522 local pdef = node and minetest.registered_nodes[node.name]
524 if pdef and pdef.on_rightclick and
525 not (placer and placer:is_player() and
526 placer:get_player_control().sneak) then
527 return pdef.on_rightclick(pos, node, placer, itemstack, pointed_thing)
530 if not pdef or not pdef.buildable_to then
531 pos = pointed_thing.above
532 node = minetest.get_node_or_nil(pos)
533 pdef = node and minetest.registered_nodes[node.name]
534 if not pdef or not pdef.buildable_to then
539 local player_name = placer and placer:get_player_name() or ""
540 -- Check sapling position for protection
541 if minetest.is_protected(pos, player_name) then
542 minetest.record_protection_violation(pos, player_name)
545 -- Check tree volume for protection
546 if minetest.is_area_protected(
547 vector.add(pos, minp_relative),
548 vector.add(pos, maxp_relative),
551 minetest.record_protection_violation(pos, player_name)
552 -- Print extra information to explain
553 minetest.chat_send_player(player_name, "Tree will intersect protection")
557 minetest.log("action", player_name .. " places node "
558 .. sapling_name .. " at " .. minetest.pos_to_string(pos))
560 local take_item = not (creative and creative.is_enabled_for
561 and creative.is_enabled_for(player_name))
562 local newnode = {name = sapling_name}
563 local ndef = minetest.registered_nodes[sapling_name]
564 minetest.set_node(pos, newnode)
567 if ndef and ndef.after_place_node then
568 -- Deepcopy place_to and pointed_thing because callback can modify it
569 if ndef.after_place_node(table.copy(pos), placer,
570 itemstack, table.copy(pointed_thing)) then
576 for _, callback in ipairs(minetest.registered_on_placenodes) do
577 -- Deepcopy pos, node and pointed_thing because callback can modify them
578 if callback(table.copy(pos), table.copy(newnode),
579 placer, table.copy(node or {}),
580 itemstack, table.copy(pointed_thing)) then
586 itemstack:take_item()