More variations of mega spruces and large oaks
[MineClone/MineClone2.git] / mods / ITEMS / mcl_core / functions.lua
bloba518bd37dfae44de3d04096cd800061ef17c26a8
1 --
2 -- Lava vs water interactions
3 --
5 local mg_name = minetest.get_mapgen_setting("mg_name")
7 minetest.register_abm({
8 label = "Lava cooling",
9 nodenames = {"group:lava"},
10 neighbors = {"group:water"},
11 interval = 1,
12 chance = 1,
13 action = function(pos, node, active_object_count, active_object_count_wider)
14 local water = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+1, z=pos.z+1}, "group:water")
16 local lavatype = minetest.registered_nodes[node.name].liquidtype
18 for w=1, #water do
19 local waternode = minetest.get_node(water[w])
20 local watertype = minetest.registered_nodes[waternode.name].liquidtype
21 -- Lava on top of water: Water turns into stone
22 if water[w].y < pos.y and water[w].x == pos.x and water[w].z == pos.z then
23 minetest.set_node(water[w], {name="mcl_core:stone"})
24 minetest.sound_play("fire_extinguish_flame", {pos = water[w], gain = 0.25, max_hear_distance = 16})
25 -- Flowing lava vs water on same level: Lava turns into cobblestone
26 elseif lavatype == "flowing" and water[w].y == pos.y and (water[w].x == pos.x or water[w].z == pos.z) then
27 minetest.set_node(pos, {name="mcl_core:cobble"})
28 minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16})
29 -- Lava source vs flowing water above or horizontally neighbored: Lava turns into obsidian
30 elseif lavatype == "source" and
31 ((water[w].y > pos.y and water[w].x == pos.x and water[w].z == pos.z) or
32 (water[w].y == pos.y and (water[w].x == pos.x or water[w].z == pos.z))) then
33 minetest.set_node(pos, {name="mcl_core:obsidian"})
34 minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16})
35 -- Flowing water above flowing lava: Lava turns into cobblestone
36 elseif watertype == "flowing" and lavatype == "flowing" and water[w].y > pos.y and water[w].x == pos.x and water[w].z == pos.z then
37 minetest.set_node(pos, {name="mcl_core:cobble"})
38 minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16})
39 end
40 end
41 end,
45 -- Papyrus and cactus growing
48 -- Functions
49 mcl_core.grow_cactus = function(pos, node)
50 pos.y = pos.y-1
51 local name = minetest.get_node(pos).name
52 if minetest.get_item_group(name, "sand") ~= 0 then
53 pos.y = pos.y+1
54 local height = 0
55 while minetest.get_node(pos).name == "mcl_core:cactus" and height < 4 do
56 height = height+1
57 pos.y = pos.y+1
58 end
59 if height < 3 then
60 if minetest.get_node(pos).name == "air" then
61 minetest.set_node(pos, {name="mcl_core:cactus"})
62 end
63 end
64 end
65 end
67 mcl_core.grow_reeds = function(pos, node)
68 pos.y = pos.y-1
69 local name = minetest.get_node(pos).name
70 if minetest.get_item_group(name, "soil_sugarcane") ~= 0 then
71 if minetest.find_node_near(pos, 1, {"group:water"}) == nil and minetest.find_node_near(pos, 1, {"group:frosted_ice"}) == nil then
72 return
73 end
74 pos.y = pos.y+1
75 local height = 0
76 while minetest.get_node(pos).name == "mcl_core:reeds" and height < 3 do
77 height = height+1
78 pos.y = pos.y+1
79 end
80 if height < 3 then
81 if minetest.get_node(pos).name == "air" then
82 minetest.set_node(pos, {name="mcl_core:reeds"})
83 end
84 end
85 end
86 end
88 -- ABMs
91 local function drop_attached_node(p)
92 local nn = minetest.get_node(p).name
93 if nn == "air" or nn == "ignore" then
94 return
95 end
96 minetest.remove_node(p)
97 for _, item in pairs(minetest.get_node_drops(nn, "")) do
98 local pos = {
99 x = p.x + math.random()/2 - 0.25,
100 y = p.y + math.random()/2 - 0.25,
101 z = p.z + math.random()/2 - 0.25,
103 if item ~= "" then
104 minetest.add_item(pos, item)
109 -- Helper function for node actions for liquid flow
110 local liquid_flow_action = function(pos, group, action)
111 local check_detach = function(pos, xp, yp, zp)
112 local p = {x=pos.x+xp, y=pos.y+yp, z=pos.z+zp}
113 local n = minetest.get_node_or_nil(p)
114 if not n then
115 return false
117 local d = minetest.registered_nodes[n.name]
118 if not d then
119 return false
121 --[[ Check if we want to perform the liquid action.
122 * 1: Item must be in liquid group
123 * 2a: If target node is below liquid, always succeed
124 * 2b: If target node is horizontal to liquid: succeed if source, otherwise check param2 for horizontal flow direction ]]
125 local range = d.liquid_range or 8
126 if (minetest.get_item_group(n.name, group) ~= 0) and
127 ((yp > 0) or
128 (yp == 0 and ((d.liquidtype == "source") or (n.param2 > (8-range) and n.param2 < 9)))) then
129 action(pos)
132 local posses = {
133 { x=-1, y=0, z=0 },
134 { x=1, y=0, z=0 },
135 { x=0, y=0, z=-1 },
136 { x=0, y=0, z=1 },
137 { x=0, y=1, z=0 },
139 for p=1,#posses do
140 check_detach(pos, posses[p].x, posses[p].y, posses[p].z)
144 -- Drop some nodes next to flowing water, if it would flow into the node
145 minetest.register_abm({
146 label = "Wash away dig_by_water nodes by water flow",
147 nodenames = {"group:dig_by_water"},
148 neighbors = {"group:water"},
149 interval = 1,
150 chance = 1,
151 action = function(pos, node, active_object_count, active_object_count_wider)
152 liquid_flow_action(pos, "water", function(pos)
153 drop_attached_node(pos)
154 minetest.dig_node(pos)
155 end)
156 end,
159 -- Destroy some nodes next to flowing lava, if it would flow into the node
160 minetest.register_abm({
161 label = "Destroy destroy_by_lava_flow nodes by lava flow",
162 nodenames = {"group:destroy_by_lava_flow"},
163 neighbors = {"group:lava"},
164 interval = 1,
165 chance = 1,
166 action = function(pos, node, active_object_count, active_object_count_wider)
167 liquid_flow_action(pos, "lava", function(pos)
168 minetest.remove_node(pos)
169 minetest.sound_play("builtin_item_lava", {pos = pos, gain = 0.25, max_hear_distance = 16})
170 core.check_for_falling(pos)
171 end)
172 end,
175 minetest.register_abm({
176 label = "Cactus growth",
177 nodenames = {"mcl_core:cactus"},
178 neighbors = {"group:sand"},
179 interval = 25,
180 chance = 10,
181 action = function(pos)
182 mcl_core.grow_cactus(pos)
183 end,
186 minetest.register_abm({
187 label = "Sugar canes growth",
188 nodenames = {"mcl_core:reeds"},
189 neighbors = {"group:soil_sugarcane"},
190 interval = 25,
191 chance = 10,
192 action = function(pos)
193 mcl_core.grow_reeds(pos)
194 end,
198 -- Sugar canes drop
201 local timber_nodenames={"mcl_core:reeds"}
203 minetest.register_on_dignode(function(pos, node)
204 local i=1
205 while timber_nodenames[i]~=nil do
206 local np={x=pos.x, y=pos.y+1, z=pos.z}
207 while minetest.get_node(np).name==timber_nodenames[i] do
208 minetest.remove_node(np)
209 minetest.add_item(np, timber_nodenames[i])
210 np={x=np.x, y=np.y+1, z=np.z}
212 i=i+1
214 end)
216 local function air_leaf(leaftype)
217 if math.random(0, 50) == 3 then
218 return {name = "air"}
219 else
220 return {name = leaftype}
224 function mcl_core.generate_tree(pos, tree_type, two_by_two)
225 pos.y = pos.y-1
226 local nodename = minetest.get_node(pos).name
228 pos.y = pos.y+1
229 if not minetest.get_node_light(pos) then
230 return
232 local node
234 if tree_type == nil or tree_type == 1 then
235 if mg_name == "v6" then
236 mcl_core.generate_v6_oak_tree(pos)
237 else
238 mcl_core.generate_oak_tree(pos)
240 elseif tree_type == 2 and two_by_two then
241 mcl_core.generate_dark_oak_tree(pos)
242 elseif tree_type == 3 then
243 if two_by_two then
244 mcl_core.generate_huge_spruce_tree(pos)
245 else
246 if mg_name == "v6" then
247 mcl_core.generate_v6_spruce_tree(pos)
248 else
249 mcl_core.generate_spruce_tree(pos)
252 elseif tree_type == 4 then
253 mcl_core.generate_acacia_tree(pos)
254 elseif tree_type == 5 then
255 if two_by_two then
256 mcl_core.generate_huge_jungle_tree(pos)
257 else
258 if mg_name == "v6" then
259 mcl_core.generate_v6_jungle_tree(pos)
260 else
261 mcl_core.generate_jungle_tree(pos)
264 elseif tree_type == 6 then
265 mcl_core.generate_birch_tree(pos)
269 -- Classic oak in v6 style
270 function mcl_core.generate_v6_oak_tree(pos)
271 local trunk = "mcl_core:tree"
272 local leaves = "mcl_core:leaves"
273 local node = {name = ""}
274 for dy=1,4 do
275 pos.y = pos.y+dy
276 if minetest.get_node(pos).name ~= "air" then
277 return
279 pos.y = pos.y-dy
281 node = {name = trunk}
282 for dy=0,4 do
283 pos.y = pos.y+dy
284 if minetest.get_node(pos).name == "air" then
285 minetest.add_node(pos, node)
287 pos.y = pos.y-dy
290 node = {name = leaves}
291 pos.y = pos.y+3
292 local rarity = 0
293 if math.random(0, 10) == 3 then
294 rarity = 1
296 for dx=-2,2 do
297 for dz=-2,2 do
298 for dy=0,3 do
299 pos.x = pos.x+dx
300 pos.y = pos.y+dy
301 pos.z = pos.z+dz
303 if dx == 0 and dz == 0 and dy==3 then
304 if minetest.get_node(pos).name == "air" and math.random(1, 5) <= 4 then
305 minetest.add_node(pos, node)
306 minetest.add_node(pos, air_leaf(leaves))
308 elseif dx == 0 and dz == 0 and dy==4 then
309 if minetest.get_node(pos).name == "air" and math.random(1, 5) <= 4 then
310 minetest.add_node(pos, node)
311 minetest.add_node(pos, air_leaf(leaves))
313 elseif math.abs(dx) ~= 2 and math.abs(dz) ~= 2 then
314 if minetest.get_node(pos).name == "air" then
315 minetest.add_node(pos, node)
316 minetest.add_node(pos, air_leaf(leaves))
318 else
319 if math.abs(dx) ~= 2 or math.abs(dz) ~= 2 then
320 if minetest.get_node(pos).name == "air" and math.random(1, 5) <= 4 then
321 minetest.add_node(pos, node)
322 minetest.add_node(pos, air_leaf(leaves))
326 pos.x = pos.x-dx
327 pos.y = pos.y-dy
328 pos.z = pos.z-dz
334 -- Oak
335 function mcl_core.generate_oak_tree(pos)
336 local r = math.random(1, 12)
337 local path
338 local offset
339 -- Balloon oak
340 if r == 1 then
341 local s = math.random(1, 12)
342 if s == 1 then
343 -- Small balloon oak
344 path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_balloon.mts"
345 offset = { x = -2, y = -1, z = -2 }
346 else
347 -- Large balloon oak
348 local t = math.random(1, 4)
349 path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_large_"..t..".mts"
350 if t == 1 or t == 3 then
351 offset = { x = -3, y = -1, z = -3 }
352 elseif t == 2 or t == 4 then
353 offset = { x = -4, y = -1, z = -4 }
356 -- Classic oak
357 else
358 path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_classic.mts"
359 offset = { x = -2, y = -1, z = -2 }
362 minetest.place_schematic(vector.add(pos, offset), path, "random", nil, false)
365 -- Birch
366 function mcl_core.generate_birch_tree(pos)
367 local path = minetest.get_modpath("mcl_core") ..
368 "/schematics/mcl_core_birch.mts"
369 minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2}, path, "random", nil, false)
372 -- BEGIN of spruce tree generation functions --
373 -- Copied from Minetest Game 0.4.15 from the pine tree (default.generate_pine_tree)
375 -- Pine tree (=spruce tree in MCL2) from mg mapgen mod, design by sfan5, pointy top added by paramat
376 local function add_spruce_leaves(data, vi, c_air, c_ignore, c_snow, c_spruce_leaves)
377 local node_id = data[vi]
378 if node_id == c_air or node_id == c_ignore or node_id == c_snow then
379 data[vi] = c_spruce_leaves
383 function mcl_core.generate_v6_spruce_tree(pos)
384 local x, y, z = pos.x, pos.y, pos.z
385 local maxy = y + math.random(9, 13) -- Trunk top
387 local c_air = minetest.get_content_id("air")
388 local c_ignore = minetest.get_content_id("ignore")
389 local c_spruce_tree = minetest.get_content_id("mcl_core:sprucetree")
390 local c_spruce_leaves = minetest.get_content_id("mcl_core:spruceleaves")
391 local c_snow = minetest.get_content_id("mcl_core:snow")
393 local vm = minetest.get_voxel_manip()
394 local minp, maxp = vm:read_from_map(
395 {x = x - 3, y = y, z = z - 3},
396 {x = x + 3, y = maxy + 3, z = z + 3}
398 local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
399 local data = vm:get_data()
401 -- Upper branches layer
402 local dev = 3
403 for yy = maxy - 1, maxy + 1 do
404 for zz = z - dev, z + dev do
405 local vi = a:index(x - dev, yy, zz)
406 local via = a:index(x - dev, yy + 1, zz)
407 for xx = x - dev, x + dev do
408 if math.random() < 0.95 - dev * 0.05 then
409 add_spruce_leaves(data, vi, c_air, c_ignore, c_snow,
410 c_spruce_leaves)
412 vi = vi + 1
413 via = via + 1
416 dev = dev - 1
419 -- Centre top nodes
420 add_spruce_leaves(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow,
421 c_spruce_leaves)
422 add_spruce_leaves(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow,
423 c_spruce_leaves) -- Paramat added a pointy top node
425 -- Lower branches layer
426 local my = 0
427 for i = 1, 20 do -- Random 2x2 squares of leaves
428 local xi = x + math.random(-3, 2)
429 local yy = maxy + math.random(-6, -5)
430 local zi = z + math.random(-3, 2)
431 if yy > my then
432 my = yy
434 for zz = zi, zi+1 do
435 local vi = a:index(xi, yy, zz)
436 local via = a:index(xi, yy + 1, zz)
437 for xx = xi, xi + 1 do
438 add_spruce_leaves(data, vi, c_air, c_ignore, c_snow,
439 c_spruce_leaves)
440 vi = vi + 1
441 via = via + 1
446 dev = 2
447 for yy = my + 1, my + 2 do
448 for zz = z - dev, z + dev do
449 local vi = a:index(x - dev, yy, zz)
450 local via = a:index(x - dev, yy + 1, zz)
451 for xx = x - dev, x + dev do
452 if math.random() < 0.95 - dev * 0.05 then
453 add_spruce_leaves(data, vi, c_air, c_ignore, c_snow,
454 c_spruce_leaves)
456 vi = vi + 1
457 via = via + 1
460 dev = dev - 1
463 -- Trunk
464 -- Force-place lowest trunk node to replace sapling
465 data[a:index(x, y, z)] = c_spruce_tree
466 for yy = y + 1, maxy do
467 local vi = a:index(x, yy, z)
468 local node_id = data[vi]
469 if node_id == c_air or node_id == c_ignore or
470 node_id == c_spruce_leaves or node_id == c_snow then
471 data[vi] = c_spruce_tree
475 vm:set_data(data)
476 vm:write_to_map()
479 mcl_core.generate_spruce_tree = function(pos)
480 local r = math.random(1, 3)
481 local path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_spruce_"..r..".mts"
482 minetest.place_schematic({ x = pos.x - 3, y = pos.y - 1, z = pos.z - 3 }, path, "0", nil, false)
485 mcl_core.generate_huge_spruce_tree = function(pos)
486 local r1 = math.random(1, 2)
487 local r2 = math.random(1, 4)
488 local path
489 local offset = { x = -4, y = -1, z = -5 }
490 if r1 <= 2 then
491 -- Mega Spruce Taiga (full canopy)
492 path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_spruce_huge_"..r2..".mts"
493 else
494 -- Mega Taiga (leaves only at top)
495 if r2 == 1 or r2 == 3 then
496 offset = { x = -3, y = -1, z = -4}
498 path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_spruce_huge_up_"..r2..".mts"
500 minetest.place_schematic(vector.add(pos, offset), path, "0", nil, false)
503 -- END of spruce tree functions --
505 -- Acacia tree (multiple variants)
506 function mcl_core.generate_acacia_tree(pos)
507 local r = math.random(1, 7)
508 local offset = vector.new()
509 if r == 2 or r == 3 then
510 offset = { x = -4, y = -1, z = -4 }
511 elseif r == 4 or r == 6 or r == 7 then
512 offset = { x = -3, y = -1, z = -3 }
513 elseif r == 1 or r == 5 then
514 offset = { x = -5, y = -1, z = -5 }
516 local path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_acacia_"..r..".mts"
517 minetest.place_schematic(vector.add(pos, offset), path, "random", nil, false)
520 -- Generate dark oak tree with 2×2 trunk.
521 -- With pos being the lower X and the higher Z value of the trunk
522 function mcl_core.generate_dark_oak_tree(pos)
523 local path = minetest.get_modpath("mcl_core") ..
524 "/schematics/mcl_core_dark_oak.mts"
525 minetest.place_schematic({x = pos.x - 3, y = pos.y - 1, z = pos.z - 4}, path, "random", nil, false)
528 -- Helper function for jungle tree, form Minetest Game 0.4.15
529 local function add_trunk_and_leaves(data, a, pos, tree_cid, leaves_cid,
530 height, size, iters)
531 local x, y, z = pos.x, pos.y, pos.z
532 local c_air = minetest.CONTENT_AIR
533 local c_ignore = minetest.CONTENT_IGNORE
535 -- Trunk
536 data[a:index(x, y, z)] = tree_cid -- Force-place lowest trunk node to replace sapling
537 for yy = y + 1, y + height - 1 do
538 local vi = a:index(x, yy, z)
539 local node_id = data[vi]
540 if node_id == c_air or node_id == c_ignore or node_id == leaves_cid then
541 data[vi] = tree_cid
545 -- Force leaves near the trunk
546 for z_dist = -1, 1 do
547 for y_dist = -size, 1 do
548 local vi = a:index(x - 1, y + height + y_dist, z + z_dist)
549 for x_dist = -1, 1 do
550 if data[vi] == c_air or data[vi] == c_ignore then
551 data[vi] = leaves_cid
553 vi = vi + 1
558 -- Randomly add leaves in 2x2x2 clusters.
559 for i = 1, iters do
560 local clust_x = x + math.random(-size, size - 1)
561 local clust_y = y + height + math.random(-size, 0)
562 local clust_z = z + math.random(-size, size - 1)
564 for xi = 0, 1 do
565 for yi = 0, 1 do
566 for zi = 0, 1 do
567 local vi = a:index(clust_x + xi, clust_y + yi, clust_z + zi)
568 if data[vi] == c_air or data[vi] == c_ignore then
569 data[vi] = leaves_cid
577 -- Old jungle tree grow function from Minetest Game 0.4.15, imitating v6 jungle trees
578 function mcl_core.generate_v6_jungle_tree(pos)
579 --[[
580 NOTE: Jungletree-placing code is currently duplicated in the engine
581 and in games that have saplings; both are deprecated but not
582 replaced yet
583 --]]
585 local x, y, z = pos.x, pos.y, pos.z
586 local height = math.random(8, 12)
587 local c_air = minetest.get_content_id("air")
588 local c_ignore = minetest.get_content_id("ignore")
589 local c_jungletree = minetest.get_content_id("mcl_core:jungletree")
590 local c_jungleleaves = minetest.get_content_id("mcl_core:jungleleaves")
592 local vm = minetest.get_voxel_manip()
593 local minp, maxp = vm:read_from_map(
594 {x = x - 3, y = y - 1, z = z - 3},
595 {x = x + 3, y = y + height + 1, z = z + 3}
597 local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
598 local data = vm:get_data()
600 add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves, height, 3, 30)
602 -- Roots
603 for z_dist = -1, 1 do
604 local vi_1 = a:index(x - 1, y - 1, z + z_dist)
605 local vi_2 = a:index(x - 1, y, z + z_dist)
606 for x_dist = -1, 1 do
607 if math.random(1, 3) >= 2 then
608 if data[vi_1] == c_air or data[vi_1] == c_ignore then
609 data[vi_1] = c_jungletree
610 elseif data[vi_2] == c_air or data[vi_2] == c_ignore then
611 data[vi_2] = c_jungletree
614 vi_1 = vi_1 + 1
615 vi_2 = vi_2 + 1
619 vm:set_data(data)
620 vm:write_to_map()
623 function mcl_core.generate_jungle_tree(pos)
624 local path = minetest.get_modpath("mcl_core") ..
625 "/schematics/mcl_core_jungle_tree.mts"
626 minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2}, path, "random", nil, false)
629 -- Generate huge jungle tree with 2×2 trunk.
630 -- With pos being the lower X and the higher Z value of the trunk.
631 function mcl_core.generate_huge_jungle_tree(pos)
632 -- 2 variants
633 local r = math.random(1, 2)
634 local path = minetest.get_modpath("mcl_core") ..
635 "/schematics/mcl_core_jungle_tree_huge_"..r..".mts"
636 minetest.place_schematic({x = pos.x - 6, y = pos.y - 1, z = pos.z - 7}, path, "random", nil, false)
640 local grass_spread_randomizer = PseudoRandom(minetest.get_mapgen_setting("seed"))
642 ------------------------------
643 -- Spread grass blocks and mycelium on neighbor dirt
644 ------------------------------
645 minetest.register_abm({
646 label = "Grass Block and Mycelium spread",
647 nodenames = {"mcl_core:dirt"},
648 neighbors = {"air", "group:grass_block_no_snow", "mcl_core:mycelium"},
649 interval = 30,
650 chance = 20,
651 catch_up = false,
652 action = function(pos)
653 if pos == nil then
654 return
656 local can_change = false
657 local above = {x=pos.x, y=pos.y+1, z=pos.z}
658 local abovenode = minetest.get_node(above)
659 if minetest.get_item_group(abovenode.name, "liquid") ~= 0 or minetest.get_item_group(abovenode.name, "opaque") == 1 then
660 -- Never grow directly below liquids or opaque blocks
661 return
663 local light_self = minetest.get_node_light(above)
664 if not light_self then return end
665 --[[ Try to find a spreading dirt-type block (e.g. grass block or mycelium)
666 within a 3×5×3 area, with the source block being on the 2nd-topmost layer. ]]
667 local nodes = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+3, z=pos.z+1}, "group:spreading_dirt_type")
668 local p2
669 -- Nothing found ? Bail out!
670 if #nodes <= 0 then
671 return
672 else
673 p2 = nodes[grass_spread_randomizer:next(1, #nodes)]
676 -- Found it! Now check light levels!
677 local source_above = {x=p2.x, y=p2.y+1, z=p2.z}
678 local light_source = minetest.get_node_light(source_above)
679 if not light_source then return end
681 if light_self >= 4 and light_source >= 9 then
682 -- All checks passed! Let's spread the grass/mycelium!
683 local n2 = minetest.get_node(p2)
684 minetest.set_node(pos, {name=n2.name})
686 -- If this was mycelium, uproot plant above
687 if n2.name == "mcl_core:mycelium" then
688 local tad = minetest.registered_nodes[minetest.get_node(above).name]
689 if tad.groups and tad.groups.non_mycelium_plant then
690 minetest.dig_node(above)
697 -- Grass/mycelium death in darkness
698 minetest.register_abm({
699 label = "Grass Block / Mycelium in darkness",
700 nodenames = {"group:spreading_dirt_type"},
701 interval = 8,
702 chance = 50,
703 catch_up = false,
704 action = function(pos, node)
705 local above = {x = pos.x, y = pos.y + 1, z = pos.z}
706 local name = minetest.get_node(above).name
707 -- Kill grass/mycelium when below opaque block or liquid
708 if name ~= "ignore" and (minetest.get_item_group(name, "opaque") == 1 or minetest.get_item_group(name, "liquid") ~= 0) then
709 minetest.set_node(pos, {name = "mcl_core:dirt"})
714 -- Turn Grass Path and similar nodes to Dirt if a solid node is placed above it
715 minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
716 if minetest.get_item_group(newnode.name, "solid") ~= 0 or
717 minetest.get_item_group(newnode.name, "dirtifier") ~= 0 then
718 local below = {x=pos.x, y=pos.y-1, z=pos.z}
719 local belownode = minetest.get_node(below)
720 if minetest.get_item_group(belownode.name, "dirtifies_below_solid") == 1 then
721 minetest.set_node(below, {name="mcl_core:dirt"})
724 end)
726 minetest.register_abm({
727 label = "Turn Grass Path below solid block into Dirt",
728 nodenames = {"mcl_core:grass_path"},
729 neighbors = {"group:solid"},
730 interval = 8,
731 chance = 50,
732 action = function(pos, node)
733 local above = {x = pos.x, y = pos.y + 1, z = pos.z}
734 local name = minetest.get_node(above).name
735 local nodedef = minetest.registered_nodes[name]
736 if name ~= "ignore" and nodedef and (nodedef.groups and nodedef.groups.solid) then
737 minetest.set_node(pos, {name = "mcl_core:dirt"})
739 end,
742 --------------------------
743 -- Try generate tree ---
744 --------------------------
745 local treelight = 9
747 local sapling_grow_action = function(tree_id, soil_needed, one_by_one, two_by_two, sapling)
748 return function(pos)
749 -- Checks if the sapling at pos has enough light and the correct soil
750 local sapling_is_growable = function(pos)
751 local light = minetest.get_node_light(pos)
752 local soilnode = minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z})
753 local soiltype = minetest.get_item_group(soilnode.name, "soil_sapling")
754 return soiltype >= soil_needed and light and light >= treelight
756 if sapling_is_growable(pos) then
757 -- Increase and check growth stage
758 local meta = minetest.get_meta(pos)
759 local stage = meta:get_int("stage")
760 if stage == nil then stage = 0 end
761 stage = stage + 1
762 if stage >= 3 then
763 -- This sapling grows in a special way when there are 4 saplings in a 2×2 pattern
764 if two_by_two then
765 -- Check 8 surrounding saplings and try to find a 2×2 pattern
766 local is_sapling = function(pos, sapling)
767 return minetest.get_node(pos).name == sapling
769 local p2 = {x=pos.x+1, y=pos.y, z=pos.z}
770 local p3 = {x=pos.x, y=pos.y, z=pos.z-1}
771 local p4 = {x=pos.x+1, y=pos.y, z=pos.z-1}
772 local p5 = {x=pos.x-1, y=pos.y, z=pos.z-1}
773 local p6 = {x=pos.x-1, y=pos.y, z=pos.z}
774 local p7 = {x=pos.x-1, y=pos.y, z=pos.z+1}
775 local p8 = {x=pos.x, y=pos.y, z=pos.z+1}
776 local p9 = {x=pos.x+1, y=pos.y, z=pos.z+1}
777 local s2 = is_sapling(p2, sapling)
778 local s3 = is_sapling(p3, sapling)
779 local s4 = is_sapling(p4, sapling)
780 local s5 = is_sapling(p5, sapling)
781 local s6 = is_sapling(p6, sapling)
782 local s7 = is_sapling(p7, sapling)
783 local s8 = is_sapling(p8, sapling)
784 local s9 = is_sapling(p9, sapling)
785 -- In a 9×9 field there are 4 possible 2×2 squares. We check them all.
786 if s2 and s3 and s4 then
787 -- Success: Remove saplings and place tree
788 minetest.remove_node(pos, {name="air"})
789 minetest.remove_node(p2, {name="air"})
790 minetest.remove_node(p3, {name="air"})
791 minetest.remove_node(p4, {name="air"})
792 mcl_core.generate_tree(pos, tree_id, true)
793 return
794 elseif s3 and s5 and s6 then
795 minetest.remove_node(pos, {name="air"})
796 minetest.remove_node(p3, {name="air"})
797 minetest.remove_node(p5, {name="air"})
798 minetest.remove_node(p6, {name="air"})
799 mcl_core.generate_tree(p6, tree_id, true)
800 return
801 elseif s6 and s7 and s8 then
802 minetest.remove_node(pos, {name="air"})
803 minetest.remove_node(p6, {name="air"})
804 minetest.remove_node(p7, {name="air"})
805 minetest.remove_node(p8, {name="air"})
806 mcl_core.generate_tree(p7, tree_id, true)
807 return
808 elseif s2 and s8 and s9 then
809 minetest.remove_node(pos, {name="air"})
810 minetest.remove_node(p2, {name="air"})
811 minetest.remove_node(p8, {name="air"})
812 minetest.remove_node(p9, {name="air"})
813 mcl_core.generate_tree(p8, tree_id, true)
814 return
817 -- If this sapling can grow alone
818 if one_by_one then
819 -- Single sapling
820 minetest.set_node(pos, {name="air"})
821 mcl_core.generate_tree(pos, tree_id)
822 return
824 else
825 meta:set_int("stage", stage)
831 local grow_oak = sapling_grow_action(1, 1, true, false)
832 local grow_dark_oak = sapling_grow_action(2, 2, false, true, "mcl_core:darksapling")
833 local grow_jungle_tree = sapling_grow_action(5, 1, true, true, "mcl_core:junglesapling")
834 local grow_acacia = sapling_grow_action(4, 2, true, false)
835 local grow_spruce = sapling_grow_action(3, 1, true, true, "mcl_core:sprucesapling")
836 local grow_birch = sapling_grow_action(6, 1, true, false)
838 -- Attempts to grow the sapling at the specified position
839 -- pos: Position
840 -- node: Node table of the node at this position, from minetest.get_node
841 -- Returns true on success and false on failure
842 mcl_core.grow_sapling = function(pos, node)
843 local grow
844 if node.name == "mcl_core:sapling" then
845 grow = grow_oak
846 elseif node.name == "mcl_core:darksapling" then
847 grow = grow_dark_oak
848 elseif node.name == "mcl_core:junglesapling" then
849 grow = grow_jungle_tree
850 elseif node.name == "mcl_core:acaciasapling" then
851 grow = grow_acacia
852 elseif node.name == "mcl_core:sprucesapling" then
853 grow = grow_spruce
854 elseif node.name == "mcl_core:birchsapling" then
855 grow = grow_birch
857 if grow then
858 grow(pos)
859 return true
860 else
861 return false
865 -- TODO: Use better tree models for everything
866 -- TODO: Support 2×2 saplings
868 -- Oak tree
869 minetest.register_abm({
870 label = "Oak tree growth",
871 nodenames = {"mcl_core:sapling"},
872 neighbors = {"group:soil_sapling"},
873 interval = 25,
874 chance = 2,
875 action = grow_oak,
878 -- Dark oak tree
879 minetest.register_abm({
880 label = "Dark oak tree growth",
881 nodenames = {"mcl_core:darksapling"},
882 neighbors = {"group:soil_sapling"},
883 interval = 25,
884 chance = 2,
885 action = grow_dark_oak,
888 -- Jungle Tree
889 minetest.register_abm({
890 label = "Jungle tree growth",
891 nodenames = {"mcl_core:junglesapling"},
892 neighbors = {"group:soil_sapling"},
893 interval = 25,
894 chance = 2,
895 action = grow_jungle_tree,
898 -- Spruce tree
899 minetest.register_abm({
900 label = "Spruce tree growth",
901 nodenames = {"mcl_core:sprucesapling"},
902 neighbors = {"group:soil_sapling"},
903 interval = 25,
904 chance = 2,
905 action = grow_spruce
908 -- Birch tree
909 minetest.register_abm({
910 label = "Birch tree growth",
911 nodenames = {"mcl_core:birchsapling"},
912 neighbors = {"group:soil_sapling"},
913 interval = 25,
914 chance = 2,
915 action = grow_birch,
918 -- Acacia tree
919 minetest.register_abm({
920 label = "Acacia tree growth",
921 nodenames = {"mcl_core:acaciasapling"},
922 neighbors = {"group:soil_sapling"},
923 interval = 20,
924 chance = 2,
925 action = grow_acacia,
928 ---------------------
929 -- Vine generating --
930 ---------------------
931 minetest.register_abm({
932 label = "Vines growth",
933 nodenames = {"mcl_core:vine"},
934 interval = 47,
935 chance = 4,
936 action = function(pos, node, active_object_count, active_object_count_wider)
938 -- First of all, check if we are even supported, otherwise, let's die!
939 if not mcl_core.check_vines_supported(pos, node) then
940 minetest.remove_node(pos)
941 core.check_for_falling(pos)
942 return
945 -- Add vines below pos (if empty)
946 local spread_down = function(origin, target, dir, node)
947 if math.random(1, 2) == 1 then
948 if minetest.get_node(target).name == "air" then
949 minetest.add_node(target, {name = "mcl_core:vine", param2 = node.param2})
954 -- Add vines above pos if it is backed up
955 local spread_up = function(origin, target, dir, node)
956 local vines_in_area = minetest.find_nodes_in_area({x=origin.x-4, y=origin.y-1, z=origin.z-4}, {x=origin.x+4, y=origin.y+1, z=origin.z+4}, "mcl_core:vine")
957 -- Less then 4 vines blocks around the ticked vines block (remember the ticked block is counted by above function as well)
958 if #vines_in_area < 5 then
959 if math.random(1, 2) == 1 then
960 if minetest.get_node(target).name == "air" then
961 local backup_dir = minetest.wallmounted_to_dir(node.param2)
962 local backup = vector.subtract(target, backup_dir)
963 local backupnodename = minetest.get_node(backup).name
965 -- Check if the block above is supported
966 if mcl_core.supports_vines(backupnodename) then
967 minetest.add_node(target, {name = "mcl_core:vine", param2 = node.param2})
974 local spread_horizontal = function(origin, target, dir, node)
975 local vines_in_area = minetest.find_nodes_in_area({x=origin.x-4, y=origin.y-1, z=origin.z-4}, {x=origin.x+4, y=origin.y+1, z=origin.z+4}, "mcl_core:vine")
976 -- Less then 4 vines blocks around the ticked vines block (remember the ticked block is counted by above function as well)
977 if #vines_in_area < 5 then
978 -- Spread horizontally
979 local backup_dir = minetest.wallmounted_to_dir(node.param2)
980 if not vector.equals(backup_dir, dir) then
981 local target_node = minetest.get_node(target)
982 if target_node.name == "air" then
983 local backup = vector.add(target, backup_dir)
984 local backupnodename = minetest.get_node(backup).name
985 if mcl_core.supports_vines(backupnodename) then
986 minetest.add_node(target, {name = "mcl_core:vine", param2 = node.param2})
993 local directions = {
994 { { x= 1, y= 0, z= 0 }, spread_horizontal },
995 { { x=-1, y= 0, z= 0 }, spread_horizontal },
996 { { x= 0, y= 1, z= 0 }, spread_up },
997 { { x= 0, y=-1, z= 0 }, spread_down },
998 { { x= 0, y= 0, z= 1 }, spread_horizontal },
999 { { x= 0, y= 0, z=-1 }, spread_horizontal },
1002 local d = math.random(1, #directions)
1003 local dir = directions[d][1]
1004 local spread = directions[d][2]
1006 spread(pos, vector.add(pos, dir), dir, node)
1010 -- Returns true of the node supports vines
1011 mcl_core.supports_vines = function(nodename)
1012 local def = minetest.registered_nodes[nodename]
1013 -- Rules: 1) walkable 2) full cube
1014 return def.walkable and
1015 (def.node_box == nil or def.node_box.type == "regular") and
1016 (def.collision_box == nil or def.collision_box.type == "regular")
1019 -- Leaf Decay
1021 -- To enable leaf decay for a node, add it to the "leafdecay" group.
1023 -- The rating of the group determines how far from a node in the group "tree"
1024 -- the node can be without decaying.
1026 -- If param2 of the node is ~= 0, the node will always be preserved. Thus, if
1027 -- the player places a node of that kind, you will want to set param2=1 or so.
1030 mcl_core.leafdecay_trunk_cache = {}
1031 mcl_core.leafdecay_enable_cache = true
1032 -- Spread the load of finding trunks
1033 mcl_core.leafdecay_trunk_find_allow_accumulator = 0
1035 minetest.register_globalstep(function(dtime)
1036 local finds_per_second = 5000
1037 mcl_core.leafdecay_trunk_find_allow_accumulator =
1038 math.floor(dtime * finds_per_second)
1039 end)
1041 minetest.register_abm({
1042 label = "Leaf decay",
1043 nodenames = {"group:leafdecay"},
1044 neighbors = {"air", "group:liquid"},
1045 -- A low interval and a high inverse chance spreads the load
1046 interval = 2,
1047 chance = 5,
1049 action = function(p0, node, _, _)
1050 local do_preserve = false
1051 local d = minetest.registered_nodes[node.name].groups.leafdecay
1052 if not d or d == 0 then
1053 return
1055 local n0 = minetest.get_node(p0)
1056 if n0.param2 ~= 0 then
1057 -- Prevent leafdecay for player-placed leaves.
1058 -- param2 is set to 1 after it was placed by the player
1059 return
1061 local p0_hash = nil
1062 if mcl_core.leafdecay_enable_cache then
1063 p0_hash = minetest.hash_node_position(p0)
1064 local trunkp = mcl_core.leafdecay_trunk_cache[p0_hash]
1065 if trunkp then
1066 local n = minetest.get_node(trunkp)
1067 local reg = minetest.registered_nodes[n.name]
1068 -- Assume ignore is a trunk, to make the thing work at the border of the active area
1069 if n.name == "ignore" or (reg and reg.groups.tree and reg.groups.tree ~= 0) then
1070 return
1072 -- Cache is invalid
1073 table.remove(mcl_core.leafdecay_trunk_cache, p0_hash)
1076 if mcl_core.leafdecay_trunk_find_allow_accumulator <= 0 then
1077 return
1079 mcl_core.leafdecay_trunk_find_allow_accumulator =
1080 mcl_core.leafdecay_trunk_find_allow_accumulator - 1
1081 -- Assume ignore is a trunk, to make the thing work at the border of the active area
1082 local p1 = minetest.find_node_near(p0, d, {"ignore", "group:tree"})
1083 if p1 then
1084 do_preserve = true
1085 if mcl_core.leafdecay_enable_cache then
1086 -- Cache the trunk
1087 mcl_core.leafdecay_trunk_cache[p0_hash] = p1
1090 if not do_preserve then
1091 -- Drop stuff other than the node itself
1092 local itemstacks = minetest.get_node_drops(n0.name)
1093 for _, itemname in ipairs(itemstacks) do
1094 local p_drop = {
1095 x = p0.x - 0.5 + math.random(),
1096 y = p0.y - 0.5 + math.random(),
1097 z = p0.z - 0.5 + math.random(),
1099 minetest.add_item(p_drop, itemname)
1101 -- Remove node
1102 minetest.remove_node(p0)
1103 core.check_for_falling(p0)
1105 -- Kill depending vines immediately to skip the vines decay delay
1106 local surround = {
1107 { x = 0, y = 0, z = -1 },
1108 { x = 0, y = 0, z = 1 },
1109 { x = -1, y = 0, z = 0 },
1110 { x = 1, y = 0, z = 0 },
1111 { x = 0, y = -1, z = -1 },
1113 for s=1, #surround do
1114 local spos = vector.add(p0, surround[s])
1115 local maybe_vine = minetest.get_node(spos)
1116 local surround_inverse = vector.multiply(surround[s], -1)
1117 if maybe_vine.name == "mcl_core:vine" and (not mcl_core.check_vines_supported(spos, maybe_vine)) then
1118 minetest.remove_node(spos)
1119 core.check_for_falling(spos)
1126 -- Remove vines which are not supported by anything, similar to leaf decay.
1127 --[[ TODO: Vines are supposed to die immediately when they supporting block is destroyed.
1128 But doing this in Minetest would be too complicated / hacky. This vines decay is a simple
1129 way to make sure that all floating vines are destroyed eventually. ]]
1130 minetest.register_abm({
1131 label = "Vines decay",
1132 nodenames = {"mcl_core:vine"},
1133 neighbors = {"air"},
1134 -- A low interval and a high inverse chance spreads the load
1135 interval = 4,
1136 chance = 8,
1137 action = function(p0, node, _, _)
1138 if not mcl_core.check_vines_supported(p0, node) then
1139 -- Vines must die!
1140 minetest.remove_node(p0)
1141 -- Just in case a falling node happens to float above vines
1142 core.check_for_falling(p0)
1147 -- Melt snow
1148 minetest.register_abm({
1149 label = "Top snow and ice melting",
1150 nodenames = {"mcl_core:snow", "mcl_core:ice"},
1151 interval = 16,
1152 chance = 8,
1153 action = function(pos, node)
1154 if minetest.get_node_light(pos, 0) >= 12 then
1155 minetest.remove_node(pos)
1160 --[[ Call this for vines nodes only.
1161 Given the pos and node of a vines node, this returns true if the vines are supported
1162 and false if the vines are currently floating.
1163 Vines are considered “supported” if they face a walkable+solid block or “hang” from a vines node above. ]]
1164 function mcl_core.check_vines_supported(pos, node)
1165 local supported = false
1166 local dir = minetest.wallmounted_to_dir(node.param2)
1167 local pos1 = vector.add(pos, dir)
1168 local node_neighbor = minetest.get_node(pos1)
1169 -- Check if vines are attached to a solid block.
1170 -- If ignore, we assume its solid.
1171 if node_neighbor.name == "ignore" or mcl_core.supports_vines(node_neighbor.name) then
1172 supported = true
1173 elseif dir.y == 0 then
1174 -- Vines are not attached, now we check if the vines are “hanging” below another vines block
1175 -- of equal orientation.
1176 local pos2 = vector.add(pos, {x=0, y=1, z=0})
1177 local node2 = minetest.get_node(pos2)
1178 -- Again, ignore means we assume its supported
1179 if node2.name == "ignore" or (node2.name == "mcl_core:vine" and node2.param2 == node.param2) then
1180 supported = true
1183 return supported
1186 ---- FUNCTIONS FOR SNOWED NODES ----
1187 -- These are nodes which change their appearence when they are below a snow cover
1188 -- and turn back into “normal” when the snow cover is removed.
1190 -- Registers a snowed variant of a node (e.g. grass block, podzol, mycelium).
1191 -- * itemstring_snowed: Itemstring of the snowed node to add
1192 -- * itemstring_clear: Itemstring of the original “clear” node without snow
1193 -- * tiles: Optional custom tiles
1194 -- * sounds: Optional custom sounds
1196 -- The snowable nodes also MUST have _mcl_snowed defined to contain the name
1197 -- of the snowed node.
1198 mcl_core.register_snowed_node = function(itemstring_snowed, itemstring_clear, tiles, sounds)
1199 local def = table.copy(minetest.registered_nodes[itemstring_clear])
1200 local create_doc_alias
1201 if def.description then
1202 create_doc_alias = true
1203 else
1204 create_doc_alias = false
1206 -- Just some group clearing
1207 def.description = nil
1208 def._doc_items_longdesc = nil
1209 def._doc_items_usagehelp = nil
1210 def._doc_items_create_entry = false
1211 def.groups.not_in_creative_inventory = 1
1212 if def.groups.grass_block == 1 then
1213 def.groups.grass_block_no_snow = nil
1214 def.groups.grass_block_snow = 1
1217 -- Enderman must never take this because this block is supposed to be always buried below snow.
1218 def.groups.enderman_takable = nil
1220 -- Snowed blocks never spread
1221 def.groups.spreading_dirt_type = nil
1223 -- Add the clear node to the item definition for easy lookup
1224 def._mcl_snowless = itemstring_clear
1226 -- Note: _mcl_snowed must be added to the clear node manually!
1228 if not tiles then
1229 def.tiles = {"default_snow.png", "default_dirt.png", "mcl_core_grass_side_snowed.png"}
1230 else
1231 def.tiles = tiles
1233 if not sounds then
1234 def.sounds = mcl_sounds.node_sound_dirt_defaults({
1235 footstep = { name = "pedology_snow_soft_footstep", gain = 0.5 }
1237 else
1238 def.sounds = sounds
1241 -- Register stuff
1242 minetest.register_node(itemstring_snowed, def)
1244 if create_doc_alias and minetest.get_modpath("doc") then
1245 doc.add_entry_alias("nodes", itemstring_clear, "nodes", itemstring_snowed)
1249 -- Reverts a snowed dirtlike node at pos to its original snow-less form.
1250 -- This function assumes there is no snow cover node above. This function
1251 -- MUST NOT be called if there is a snow cover node above pos.
1252 mcl_core.clear_snow_dirt = function(pos, node)
1253 local def = minetest.registered_nodes[node.name]
1254 if def._mcl_snowless then
1255 minetest.swap_node(pos, {name = def._mcl_snowless})
1259 ---- [[[[[ Functions for snowable nodes (nodes that can become snowed). ]]]]] ----
1260 -- Always add these for snowable nodes.
1262 -- on_construct
1263 -- Makes constructed snowable node snowed if placed below a snow cover node.
1264 mcl_core.on_snowable_construct = function(pos)
1265 -- Myself
1266 local node = minetest.get_node(pos)
1268 -- Above
1269 local apos = {x=pos.x, y=pos.y+1, z=pos.z}
1270 local anode = minetest.get_node(apos)
1272 -- Make snowed if needed
1273 if minetest.get_item_group(anode.name, "snow_cover") == 1 then
1274 local def = minetest.registered_nodes[node.name]
1275 if def._mcl_snowed then
1276 minetest.swap_node(pos, {name = def._mcl_snowed})
1282 ---- [[[[[ Functions for snow cover nodes. ]]]]] ----
1284 -- A snow cover node is a node which turns a snowed dirtlike --
1285 -- node into its snowed form while it is placed above.
1286 -- MCL2's snow cover nodes are Top Snow (mcl_core:snow) and Snow (mcl_core:snowblock).
1288 -- Always add the following functions to snow cover nodes:
1290 -- on_construct
1291 -- Makes snowable node below snowed.
1292 mcl_core.on_snow_construct = function(pos)
1293 local npos = {x=pos.x, y=pos.y-1, z=pos.z}
1294 local node = minetest.get_node(npos)
1295 local def = minetest.registered_nodes[node.name]
1296 if def._mcl_snowed then
1297 minetest.swap_node(npos, {name = def._mcl_snowed})
1300 -- after_destruct
1301 -- Clears snowed dirtlike node below.
1302 mcl_core.after_snow_destruct = function(pos)
1303 local nn = minetest.get_node(pos).name
1304 -- No-op if snow was replaced with snow
1305 if minetest.get_item_group(nn, "snow_cover") == 1 then
1306 return
1308 local npos = {x=pos.x, y=pos.y-1, z=pos.z}
1309 local node = minetest.get_node(npos)
1310 mcl_core.clear_snow_dirt(npos, node)