2 -- Lava vs water interactions
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"},
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
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})
45 -- Papyrus and cactus growing
49 mcl_core
.grow_cactus
= function(pos
, node
)
51 local name
= minetest
.get_node(pos
).name
52 if minetest
.get_item_group(name
, "sand") ~= 0 then
55 while minetest
.get_node(pos
).name
== "mcl_core:cactus" and height
< 4 do
60 if minetest
.get_node(pos
).name
== "air" then
61 minetest
.set_node(pos
, {name
="mcl_core:cactus"})
67 mcl_core
.grow_reeds
= function(pos
, node
)
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
76 while minetest
.get_node(pos
).name
== "mcl_core:reeds" and height
< 3 do
81 if minetest
.get_node(pos
).name
== "air" then
82 minetest
.set_node(pos
, {name
="mcl_core:reeds"})
91 local function drop_attached_node(p
)
92 local nn
= minetest
.get_node(p
).name
93 if nn
== "air" or nn
== "ignore" then
96 minetest
.remove_node(p
)
97 for _
, item
in pairs(minetest
.get_node_drops(nn
, "")) do
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,
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
)
117 local d
= minetest
.registered_nodes
[n
.name
]
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
128 (yp
== 0 and ((d
.liquidtype
== "source") or (n
.param2
> (8-range
) and n
.param2
< 9)))) then
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"},
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
)
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"},
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
)
175 minetest
.register_abm({
176 label
= "Cactus growth",
177 nodenames
= {"mcl_core:cactus"},
178 neighbors
= {"group:sand"},
181 action
= function(pos
)
182 mcl_core
.grow_cactus(pos
)
186 minetest
.register_abm({
187 label
= "Sugar canes growth",
188 nodenames
= {"mcl_core:reeds"},
189 neighbors
= {"group:soil_sugarcane"},
192 action
= function(pos
)
193 mcl_core
.grow_reeds(pos
)
201 local timber_nodenames
={"mcl_core:reeds"}
203 minetest
.register_on_dignode(function(pos
, node
)
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
}
216 local function air_leaf(leaftype
)
217 if math
.random(0, 50) == 3 then
218 return {name
= "air"}
220 return {name
= leaftype
}
224 function mcl_core
.generate_tree(pos
, tree_type
, two_by_two
)
226 local nodename
= minetest
.get_node(pos
).name
229 if not minetest
.get_node_light(pos
) then
234 if tree_type
== nil or tree_type
== 1 then
235 if mg_name
== "v6" then
236 mcl_core
.generate_v6_oak_tree(pos
)
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
244 mcl_core
.generate_huge_spruce_tree(pos
)
246 if mg_name
== "v6" then
247 mcl_core
.generate_v6_spruce_tree(pos
)
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
256 mcl_core
.generate_huge_jungle_tree(pos
)
258 if mg_name
== "v6" then
259 mcl_core
.generate_v6_jungle_tree(pos
)
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
= ""}
276 if minetest
.get_node(pos
).name
~= "air" then
281 node
= {name
= trunk
}
284 if minetest
.get_node(pos
).name
== "air" then
285 minetest
.add_node(pos
, node
)
290 node
= {name
= leaves
}
293 if math
.random(0, 10) == 3 then
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
))
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
))
335 function mcl_core
.generate_oak_tree(pos
)
336 local r
= math
.random(1, 12)
341 local s
= math
.random(1, 12)
344 path
= minetest
.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_balloon.mts"
345 offset
= { x
= -2, y
= -1, z
= -2 }
348 local t
= math
.random(1, 2)
349 path
= minetest
.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_large_"..t
..".mts"
351 offset
= { x
= -3, y
= -1, z
= -3 }
353 offset
= { x
= -4, y
= -1, z
= -4 }
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)
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
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
,
420 add_spruce_leaves(data
, a
:index(x
, maxy
+ 1, z
), c_air
, c_ignore
, c_snow
,
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
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)
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
,
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
,
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
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, 3)
489 local offset
= { x
= -4, y
= -1, z
= -5 }
491 -- Mega Spruce Taiga (full canopy)
492 path
= minetest
.get_modpath("mcl_core") .. "/schematics/mcl_core_spruce_huge_"..r2
..".mts"
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
,
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
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
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
558 -- Randomly add leaves in 2x2x2 clusters.
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)
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
)
580 NOTE: Jungletree-placing code is currently duplicated in the engine
581 and in games that have saplings; both are deprecated but not
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)
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
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
)
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"},
652 action
= function(pos
)
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
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")
669 -- Nothing found ? Bail out!
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"},
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"})
726 minetest
.register_abm({
727 label
= "Turn Grass Path below solid block into Dirt",
728 nodenames
= {"mcl_core:grass_path"},
729 neighbors
= {"group:solid"},
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"})
742 --------------------------
743 -- Try generate tree ---
744 --------------------------
747 local sapling_grow_action
= function(tree_id
, soil_needed
, one_by_one
, two_by_two
, sapling
)
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
763 -- This sapling grows in a special way when there are 4 saplings in a 2×2 pattern
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)
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)
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)
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)
817 -- If this sapling can grow alone
820 minetest
.set_node(pos
, {name
="air"})
821 mcl_core
.generate_tree(pos
, tree_id
)
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
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
)
844 if node
.name
== "mcl_core:sapling" then
846 elseif node
.name
== "mcl_core:darksapling" then
848 elseif node
.name
== "mcl_core:junglesapling" then
849 grow
= grow_jungle_tree
850 elseif node
.name
== "mcl_core:acaciasapling" then
852 elseif node
.name
== "mcl_core:sprucesapling" then
854 elseif node
.name
== "mcl_core:birchsapling" then
865 -- TODO: Use better tree models for everything
866 -- TODO: Support 2×2 saplings
869 minetest
.register_abm({
870 label
= "Oak tree growth",
871 nodenames
= {"mcl_core:sapling"},
872 neighbors
= {"group:soil_sapling"},
879 minetest
.register_abm({
880 label
= "Dark oak tree growth",
881 nodenames
= {"mcl_core:darksapling"},
882 neighbors
= {"group:soil_sapling"},
885 action
= grow_dark_oak
,
889 minetest
.register_abm({
890 label
= "Jungle tree growth",
891 nodenames
= {"mcl_core:junglesapling"},
892 neighbors
= {"group:soil_sapling"},
895 action
= grow_jungle_tree
,
899 minetest
.register_abm({
900 label
= "Spruce tree growth",
901 nodenames
= {"mcl_core:sprucesapling"},
902 neighbors
= {"group:soil_sapling"},
909 minetest
.register_abm({
910 label
= "Birch tree growth",
911 nodenames
= {"mcl_core:birchsapling"},
912 neighbors
= {"group:soil_sapling"},
919 minetest
.register_abm({
920 label
= "Acacia tree growth",
921 nodenames
= {"mcl_core:acaciasapling"},
922 neighbors
= {"group:soil_sapling"},
925 action
= grow_acacia
,
928 ---------------------
929 -- Vine generating --
930 ---------------------
931 minetest
.register_abm({
932 label
= "Vines growth",
933 nodenames
= {"mcl_core:vine"},
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
)
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
})
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")
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
)
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
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
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
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
]
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
1073 table.remove(mcl_core
.leafdecay_trunk_cache
, p0_hash
)
1076 if mcl_core
.leafdecay_trunk_find_allow_accumulator
<= 0 then
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"})
1085 if mcl_core
.leafdecay_enable_cache
then
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
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
)
1102 minetest
.remove_node(p0
)
1103 core
.check_for_falling(p0
)
1105 -- Kill depending vines immediately to skip the vines decay delay
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
1137 action
= function(p0
, node
, _
, _
)
1138 if not mcl_core
.check_vines_supported(p0
, node
) then
1140 minetest
.remove_node(p0
)
1141 -- Just in case a falling node happens to float above vines
1142 core
.check_for_falling(p0
)
1148 minetest
.register_abm({
1149 label
= "Top snow and ice melting",
1150 nodenames
= {"mcl_core:snow", "mcl_core:ice"},
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
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
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
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!
1229 def
.tiles
= {"default_snow.png", "default_dirt.png", "mcl_core_grass_side_snowed.png"}
1234 def
.sounds
= mcl_sounds
.node_sound_dirt_defaults({
1235 footstep
= { name
= "pedology_snow_soft_footstep", gain
= 0.5 }
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.
1263 -- Makes constructed snowable node snowed if placed below a snow cover node.
1264 mcl_core
.on_snowable_construct
= function(pos
)
1266 local node
= minetest
.get_node(pos
)
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:
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
})
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
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
)