forked from VoxeLibre/VoxeLibre
Check if there is room for tree to grow
Previously tree saplings would grow regardless of the blocks above it. This could be abused to destroy bedrock and other nodes. To solve this, checks are added for nodes around the trees. The volume checked depends on the tree type. The volume for each tree has been set so reject trees where branches would grow into other nodes. Some nodes types like torches, plants and wood are ignored in the checks. Saplings in a 2x2 formation will grow into normal trees if there is no room for a huge tree. Oak trees has separate checks for balloon oak trees and normal oak trees, if there is no room for a balloon oak, it becomes a normal oak tree.
This commit is contained in:
parent
547080bd98
commit
2e79f1f0ed
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
local mg_name = minetest.get_mapgen_setting("mg_name")
|
local mg_name = minetest.get_mapgen_setting("mg_name")
|
||||||
|
|
||||||
|
local OAK_TREE_ID = 1
|
||||||
|
local DARK_OAK_TREE_ID = 2
|
||||||
|
local SPRUCE_TREE_ID = 3
|
||||||
|
local ACACIA_TREE_ID = 4
|
||||||
|
local JUNGLE_TREE_ID = 5
|
||||||
|
local BIRCH_TREE_ID = 6
|
||||||
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
label = "Lava cooling",
|
label = "Lava cooling",
|
||||||
nodenames = {"group:lava"},
|
nodenames = {"group:lava"},
|
||||||
|
@ -221,7 +228,97 @@ local function air_leaf(leaftype)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_core.generate_tree(pos, tree_type, two_by_two)
|
-- Check if a node stops a tree from growing. Torches, plants, wood, tree,
|
||||||
|
-- leaves and dirt does not affect tree growth.
|
||||||
|
function node_stops_growth(node)
|
||||||
|
if node.name == 'air' then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
def = minetest.registered_nodes[node.name]
|
||||||
|
if not def then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
groups = def.groups
|
||||||
|
if not groups then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if groups.plant or groups.torch or groups.dirt or groups.tree
|
||||||
|
or groups.bark or groups.leaves or groups.wood then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if a tree can grow at position. The width is the width to check
|
||||||
|
-- around the tree. A width of 3 and height of 5 will check a 3x3 area, 5
|
||||||
|
-- nodes above the sapling. If any walkable node other than dirt, wood or
|
||||||
|
-- leaves occurs in those blocks the tree cannot grow.
|
||||||
|
function check_growth_width(pos, width, height)
|
||||||
|
-- Huge tree (with even width to check) will check one more node in
|
||||||
|
-- positive x and y directions.
|
||||||
|
neg_space = math.min((width - 1) / 2)
|
||||||
|
pos_space = math.max((width - 1) / 2)
|
||||||
|
for x = -neg_space, pos_space do
|
||||||
|
for z = -neg_space, pos_space do
|
||||||
|
for y = 1, height do
|
||||||
|
local np = vector.new(
|
||||||
|
pos.x + x,
|
||||||
|
pos.y + y,
|
||||||
|
pos.z + z)
|
||||||
|
if node_stops_growth(minetest.get_node(np)) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if a tree with id can grow at a position. Options is a table of flags
|
||||||
|
-- for varieties of trees. The 'two_by_two' option is used to check if there is
|
||||||
|
-- room to generate huge trees for spruce and jungle. The 'balloon' option is
|
||||||
|
-- used to check if there is room to generate a balloon tree for oak.
|
||||||
|
function check_tree_growth(pos, tree_id, options)
|
||||||
|
local two_by_two = options and options.two_by_two
|
||||||
|
local balloon = options and options.balloon
|
||||||
|
|
||||||
|
if tree_id == OAK_TREE_ID then
|
||||||
|
if balloon then
|
||||||
|
return check_growth_width(pos, 7, 11)
|
||||||
|
else
|
||||||
|
return check_growth_width(pos, 3, 5)
|
||||||
|
end
|
||||||
|
elseif tree_id == BIRCH_TREE_ID then
|
||||||
|
return check_growth_width(pos, 3, 6)
|
||||||
|
elseif tree_id == SPRUCE_TREE_ID then
|
||||||
|
if two_by_two then
|
||||||
|
return check_growth_width(pos, 6, 20)
|
||||||
|
else
|
||||||
|
return check_growth_width(pos, 5, 11)
|
||||||
|
end
|
||||||
|
elseif tree_id == JUNGLE_TREE_ID then
|
||||||
|
if two_by_two then
|
||||||
|
return check_growth_width(pos, 8, 23)
|
||||||
|
else
|
||||||
|
return check_growth_width(pos, 3, 8)
|
||||||
|
end
|
||||||
|
elseif tree_id == ACACIA_TREE_ID then
|
||||||
|
return check_growth_width(pos, 7, 8)
|
||||||
|
elseif tree_id == DARK_OAK_TREE_ID and two_by_two then
|
||||||
|
return check_growth_width(pos, 4, 7)
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generates a tree with a type. Options is a table of flags for varieties of
|
||||||
|
-- trees. The 'two_by_two' option is used by jungle and spruce trees to
|
||||||
|
-- generate huge trees. The 'balloon' option is used by oak to generate a balloon
|
||||||
|
-- oak tree.
|
||||||
|
function mcl_core.generate_tree(pos, tree_type, options)
|
||||||
pos.y = pos.y-1
|
pos.y = pos.y-1
|
||||||
local nodename = minetest.get_node(pos).name
|
local nodename = minetest.get_node(pos).name
|
||||||
|
|
||||||
|
@ -231,15 +328,22 @@ function mcl_core.generate_tree(pos, tree_type, two_by_two)
|
||||||
end
|
end
|
||||||
local node
|
local node
|
||||||
|
|
||||||
if tree_type == nil or tree_type == 1 then
|
local two_by_two = options and options.two_by_two
|
||||||
|
local balloon = options and options.balloon
|
||||||
|
|
||||||
|
if tree_type == nil or tree_type == OAK_TREE_ID then
|
||||||
if mg_name == "v6" then
|
if mg_name == "v6" then
|
||||||
mcl_core.generate_v6_oak_tree(pos)
|
mcl_core.generate_v6_oak_tree(pos)
|
||||||
|
else
|
||||||
|
if balloon then
|
||||||
|
mcl_core.generate_balloon_oak_tree(pos)
|
||||||
else
|
else
|
||||||
mcl_core.generate_oak_tree(pos)
|
mcl_core.generate_oak_tree(pos)
|
||||||
end
|
end
|
||||||
elseif tree_type == 2 and two_by_two then
|
end
|
||||||
|
elseif tree_type == DARK_OAK_TREE_ID then
|
||||||
mcl_core.generate_dark_oak_tree(pos)
|
mcl_core.generate_dark_oak_tree(pos)
|
||||||
elseif tree_type == 3 then
|
elseif tree_type == SPRUCE_TREE_ID then
|
||||||
if two_by_two then
|
if two_by_two then
|
||||||
mcl_core.generate_huge_spruce_tree(pos)
|
mcl_core.generate_huge_spruce_tree(pos)
|
||||||
else
|
else
|
||||||
|
@ -249,9 +353,9 @@ function mcl_core.generate_tree(pos, tree_type, two_by_two)
|
||||||
mcl_core.generate_spruce_tree(pos)
|
mcl_core.generate_spruce_tree(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif tree_type == 4 then
|
elseif tree_type == ACACIA_TREE_ID then
|
||||||
mcl_core.generate_acacia_tree(pos)
|
mcl_core.generate_acacia_tree(pos)
|
||||||
elseif tree_type == 5 then
|
elseif tree_type == JUNGLE_TREE_ID then
|
||||||
if two_by_two then
|
if two_by_two then
|
||||||
mcl_core.generate_huge_jungle_tree(pos)
|
mcl_core.generate_huge_jungle_tree(pos)
|
||||||
else
|
else
|
||||||
|
@ -261,7 +365,7 @@ function mcl_core.generate_tree(pos, tree_type, two_by_two)
|
||||||
mcl_core.generate_jungle_tree(pos)
|
mcl_core.generate_jungle_tree(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif tree_type == 6 then
|
elseif tree_type == BIRCH_TREE_ID then
|
||||||
mcl_core.generate_birch_tree(pos)
|
mcl_core.generate_birch_tree(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -331,13 +435,10 @@ function mcl_core.generate_v6_oak_tree(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Oak
|
-- Ballon Oak
|
||||||
function mcl_core.generate_oak_tree(pos)
|
function mcl_core.generate_balloon_oak_tree(pos)
|
||||||
local r = math.random(1, 12)
|
|
||||||
local path
|
local path
|
||||||
local offset
|
local offset
|
||||||
-- Balloon oak
|
|
||||||
if r == 1 then
|
|
||||||
local s = math.random(1, 12)
|
local s = math.random(1, 12)
|
||||||
if s == 1 then
|
if s == 1 then
|
||||||
-- Small balloon oak
|
-- Small balloon oak
|
||||||
|
@ -353,12 +454,14 @@ function mcl_core.generate_oak_tree(pos)
|
||||||
offset = { x = -4, y = -1, z = -4 }
|
offset = { x = -4, y = -1, z = -4 }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Classic oak
|
minetest.place_schematic(vector.add(pos, offset), path, "random", nil, false)
|
||||||
else
|
|
||||||
path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_classic.mts"
|
|
||||||
offset = { x = -2, y = -1, z = -2 }
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Oak
|
||||||
|
function mcl_core.generate_oak_tree(pos)
|
||||||
|
local path = minetest.get_modpath("mcl_core") .. "/schematics/mcl_core_oak_classic.mts"
|
||||||
|
local offset = { x = -2, y = -1, z = -2 }
|
||||||
|
|
||||||
minetest.place_schematic(vector.add(pos, offset), path, "random", nil, false)
|
minetest.place_schematic(vector.add(pos, offset), path, "random", nil, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -833,41 +936,55 @@ local sapling_grow_action = function(tree_id, soil_needed, one_by_one, two_by_tw
|
||||||
local s8 = is_sapling(p8, sapling)
|
local s8 = is_sapling(p8, sapling)
|
||||||
local s9 = is_sapling(p9, sapling)
|
local s9 = is_sapling(p9, sapling)
|
||||||
-- In a 9×9 field there are 4 possible 2×2 squares. We check them all.
|
-- In a 9×9 field there are 4 possible 2×2 squares. We check them all.
|
||||||
if s2 and s3 and s4 then
|
if s2 and s3 and s4 and check_tree_growth(pos, tree_id, { two_by_two = true }) then
|
||||||
-- Success: Remove saplings and place tree
|
-- Success: Remove saplings and place tree
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
minetest.remove_node(p2)
|
minetest.remove_node(p2)
|
||||||
minetest.remove_node(p3)
|
minetest.remove_node(p3)
|
||||||
minetest.remove_node(p4)
|
minetest.remove_node(p4)
|
||||||
mcl_core.generate_tree(pos, tree_id, true)
|
mcl_core.generate_tree(pos, tree_id, { two_by_two = true })
|
||||||
return
|
return
|
||||||
elseif s3 and s5 and s6 then
|
elseif s3 and s5 and s6 and check_tree_growth(p6, tree_id, { two_by_two = true }) then
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
minetest.remove_node(p3)
|
minetest.remove_node(p3)
|
||||||
minetest.remove_node(p5)
|
minetest.remove_node(p5)
|
||||||
minetest.remove_node(p6)
|
minetest.remove_node(p6)
|
||||||
mcl_core.generate_tree(p6, tree_id, true)
|
mcl_core.generate_tree(p6, tree_id, { two_by_two = true })
|
||||||
return
|
return
|
||||||
elseif s6 and s7 and s8 then
|
elseif s6 and s7 and s8 and check_tree_growth(p7, tree_id, { two_by_two = true }) then
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
minetest.remove_node(p6)
|
minetest.remove_node(p6)
|
||||||
minetest.remove_node(p7)
|
minetest.remove_node(p7)
|
||||||
minetest.remove_node(p8)
|
minetest.remove_node(p8)
|
||||||
mcl_core.generate_tree(p7, tree_id, true)
|
mcl_core.generate_tree(p7, tree_id, { two_by_two = true })
|
||||||
return
|
return
|
||||||
elseif s2 and s8 and s9 then
|
elseif s2 and s8 and s9 and check_tree_growth(p8, tree_id, { two_by_two = true }) then
|
||||||
minetest.remove_node(pos)
|
minetest.remove_node(pos)
|
||||||
minetest.remove_node(p2)
|
minetest.remove_node(p2)
|
||||||
minetest.remove_node(p8)
|
minetest.remove_node(p8)
|
||||||
minetest.remove_node(p9)
|
minetest.remove_node(p9)
|
||||||
mcl_core.generate_tree(p8, tree_id, true)
|
mcl_core.generate_tree(p8, tree_id, { two_by_two = true })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if one_by_one and tree_id == OAK_TREE_ID then
|
||||||
|
-- There is a chance that this tree wants to grow as a balloon oak
|
||||||
|
if math.random(1, 12) == 1 then
|
||||||
|
-- Check if there is room for that
|
||||||
|
if check_tree_growth(pos, tree_id, { balloon = true }) then
|
||||||
|
minetest.set_node(pos, {name="air"})
|
||||||
|
mcl_core.generate_tree(pos, tree_id, { balloon = true })
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- If this sapling can grow alone
|
-- If this sapling can grow alone
|
||||||
if one_by_one then
|
if one_by_one and check_tree_growth(pos, tree_id) then
|
||||||
-- Single sapling
|
-- Single sapling
|
||||||
minetest.set_node(pos, {name="air"})
|
minetest.set_node(pos, {name="air"})
|
||||||
|
local r = math.random(1, 12)
|
||||||
mcl_core.generate_tree(pos, tree_id)
|
mcl_core.generate_tree(pos, tree_id)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -878,12 +995,12 @@ local sapling_grow_action = function(tree_id, soil_needed, one_by_one, two_by_tw
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local grow_oak = sapling_grow_action(1, 1, true, false)
|
local grow_oak = sapling_grow_action(OAK_TREE_ID, 1, true, false)
|
||||||
local grow_dark_oak = sapling_grow_action(2, 2, false, true, "mcl_core:darksapling")
|
local grow_dark_oak = sapling_grow_action(DARK_OAK_TREE_ID, 2, false, true, "mcl_core:darksapling")
|
||||||
local grow_jungle_tree = sapling_grow_action(5, 1, true, true, "mcl_core:junglesapling")
|
local grow_jungle_tree = sapling_grow_action(JUNGLE_TREE_ID, 1, true, true, "mcl_core:junglesapling")
|
||||||
local grow_acacia = sapling_grow_action(4, 2, true, false)
|
local grow_acacia = sapling_grow_action(ACACIA_TREE_ID, 2, true, false)
|
||||||
local grow_spruce = sapling_grow_action(3, 1, true, true, "mcl_core:sprucesapling")
|
local grow_spruce = sapling_grow_action(SPRUCE_TREE_ID, 1, true, true, "mcl_core:sprucesapling")
|
||||||
local grow_birch = sapling_grow_action(6, 1, true, false)
|
local grow_birch = sapling_grow_action(BIRCH_TREE_ID, 1, true, false)
|
||||||
|
|
||||||
-- Attempts to grow the sapling at the specified position
|
-- Attempts to grow the sapling at the specified position
|
||||||
-- pos: Position
|
-- pos: Position
|
||||||
|
|
Loading…
Reference in New Issue