6267f26027
Adds a birch-like tree to the default_game. Aspen was chosen on purpose instead of birch, as several birch tree mods already exist and choosing a different name avoids any conflicts. Schematics were made for both normal and sapling version, assuring saplings will not be able to grief. The shape of the Aspen is "fanning out at the top" and provides an easy tree to walk under, but still a somewhat thick cover. The Aspen trunk is 4 to 6 blocks tall, with up to three layers of leaves on top, making it slightly taller than an Apple tree, but shorter than a Pine tree, which provides a good blend. Textures were painted from scratch, except tree_top and _wood variants, which are color modified versions of the pine_wood variants. Appropriate licenses have been chosen for the new textures. The leaf texture is light enough to contrast other trees, but dark enough to offset against our light default grass color. The leaves are drawn in the typical minetest default fashion with plenty of transparancy, but enough definition to suggest that you're seeing something that looks like leaves. The placement of leaves in the schematic also suggests the top of the tree is sparse and you can see the sky through the leaves. Sapling texture is both traditional and different, with lush green leaves and a well-defined stem, but slightly stick-like and skinny, as these plants tend to grow up first, then out. Add fallen Aspen logs. We make these logs a minimum of 2 blocks long, and up to 3. This allows us to make these logs a place where both red and brown mushrooms can be found, to these may be attractive to players. However, the spawn rate for these has been reduced a lot compared to the other logs, to account for the scarcity of Aspen. Add stairs, slabs for these wood types as well. Mapgen will place these trees in deciduous forests only, but in a way that the biome is a range between entirely Apple trees, and mostly entirely Aspen trees, with a bias to Apple trees. To make fallen logs somewhat correlated with trees, we modify the planting of Apple trees and logs to use perlin noise and not fill ratio, otherwise you'd always end up with Apple logs in Aspen tree areas, which would be suspicious. There still is a bit of a mix.
411 lines
11 KiB
Lua
411 lines
11 KiB
Lua
--
|
|
-- Grow trees from saplings
|
|
--
|
|
|
|
-- 'Can grow' function
|
|
|
|
local random = math.random
|
|
|
|
function default.can_grow(pos)
|
|
local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
|
|
if not node_under then
|
|
return false
|
|
end
|
|
local name_under = node_under.name
|
|
local is_soil = minetest.get_item_group(name_under, "soil")
|
|
if is_soil == 0 then
|
|
return false
|
|
end
|
|
local light_level = minetest.get_node_light(pos)
|
|
if not light_level or light_level < 13 then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
-- Sapling ABM
|
|
|
|
minetest.register_abm({
|
|
nodenames = {"default:sapling", "default:junglesapling",
|
|
"default:pine_sapling", "default:acacia_sapling",
|
|
"default:aspen_sapling"},
|
|
interval = 10,
|
|
chance = 50,
|
|
action = function(pos, node)
|
|
if not default.can_grow(pos) then
|
|
return
|
|
end
|
|
|
|
local mapgen = minetest.get_mapgen_params().mgname
|
|
if node.name == "default:sapling" then
|
|
minetest.log("action", "A sapling grows into a tree at "..
|
|
minetest.pos_to_string(pos))
|
|
if mapgen == "v6" then
|
|
default.grow_tree(pos, random(1, 4) == 1)
|
|
else
|
|
default.grow_new_apple_tree(pos)
|
|
end
|
|
elseif node.name == "default:junglesapling" then
|
|
minetest.log("action", "A jungle sapling grows into a tree at "..
|
|
minetest.pos_to_string(pos))
|
|
if mapgen == "v6" then
|
|
default.grow_jungle_tree(pos)
|
|
else
|
|
default.grow_new_jungle_tree(pos)
|
|
end
|
|
elseif node.name == "default:pine_sapling" then
|
|
minetest.log("action", "A pine sapling grows into a tree at "..
|
|
minetest.pos_to_string(pos))
|
|
if mapgen == "v6" then
|
|
default.grow_pine_tree(pos)
|
|
else
|
|
default.grow_new_pine_tree(pos)
|
|
end
|
|
elseif node.name == "default:acacia_sapling" then
|
|
minetest.log("action", "An acacia sapling grows into a tree at "..
|
|
minetest.pos_to_string(pos))
|
|
default.grow_new_acacia_tree(pos)
|
|
elseif node.name == "default:aspen_sapling" then
|
|
minetest.log("action", "An aspen sapling grows into a tree at "..
|
|
minetest.pos_to_string(pos))
|
|
default.grow_new_aspen_tree(pos)
|
|
end
|
|
end
|
|
})
|
|
|
|
|
|
--
|
|
-- Tree generation
|
|
--
|
|
|
|
-- Apple tree and jungle tree trunk and leaves function
|
|
|
|
local function add_trunk_and_leaves(data, a, pos, tree_cid, leaves_cid,
|
|
height, size, iters, is_apple_tree)
|
|
local x, y, z = pos.x, pos.y, pos.z
|
|
local c_air = minetest.get_content_id("air")
|
|
local c_ignore = minetest.get_content_id("ignore")
|
|
local c_apple = minetest.get_content_id("default:apple")
|
|
|
|
-- Trunk
|
|
data[a:index(x, y, z)] = tree_cid -- Force-place lowest trunk node to replace sapling
|
|
for yy = y + 1, y + height - 1 do
|
|
local vi = a:index(x, yy, z)
|
|
local node_id = data[vi]
|
|
if node_id == c_air or node_id == c_ignore or node_id == leaves_cid then
|
|
data[vi] = tree_cid
|
|
end
|
|
end
|
|
|
|
-- Force leaves near the trunk
|
|
for z_dist = -1, 1 do
|
|
for y_dist = -size, 1 do
|
|
local vi = a:index(x - 1, y + height + y_dist, z + z_dist)
|
|
for x_dist = -1, 1 do
|
|
if data[vi] == c_air or data[vi] == c_ignore then
|
|
if is_apple_tree and random(1, 8) == 1 then
|
|
data[vi] = c_apple
|
|
else
|
|
data[vi] = leaves_cid
|
|
end
|
|
end
|
|
vi = vi + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Randomly add leaves in 2x2x2 clusters.
|
|
for i = 1, iters do
|
|
local clust_x = x + random(-size, size - 1)
|
|
local clust_y = y + height + random(-size, 0)
|
|
local clust_z = z + random(-size, size - 1)
|
|
|
|
for xi = 0, 1 do
|
|
for yi = 0, 1 do
|
|
for zi = 0, 1 do
|
|
local vi = a:index(clust_x + xi, clust_y + yi, clust_z + zi)
|
|
if data[vi] == c_air or data[vi] == c_ignore then
|
|
if is_apple_tree and random(1, 8) == 1 then
|
|
data[vi] = c_apple
|
|
else
|
|
data[vi] = leaves_cid
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Apple tree
|
|
|
|
function default.grow_tree(pos, is_apple_tree, bad)
|
|
--[[
|
|
NOTE: Tree-placing code is currently duplicated in the engine
|
|
and in games that have saplings; both are deprecated but not
|
|
replaced yet
|
|
--]]
|
|
if bad then
|
|
error("Deprecated use of default.grow_tree")
|
|
end
|
|
|
|
local x, y, z = pos.x, pos.y, pos.z
|
|
local height = random(4, 5)
|
|
local c_tree = minetest.get_content_id("default:tree")
|
|
local c_leaves = minetest.get_content_id("default:leaves")
|
|
|
|
local vm = minetest.get_voxel_manip()
|
|
local minp, maxp = vm:read_from_map(
|
|
{x = pos.x - 2, y = pos.y, z = pos.z - 2},
|
|
{x = pos.x + 2, y = pos.y + height + 1, z = pos.z + 2}
|
|
)
|
|
local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
|
|
local data = vm:get_data()
|
|
|
|
add_trunk_and_leaves(data, a, pos, c_tree, c_leaves, height, 2, 8, is_apple_tree)
|
|
|
|
vm:set_data(data)
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
end
|
|
|
|
|
|
-- Jungle tree
|
|
|
|
function default.grow_jungle_tree(pos, bad)
|
|
--[[
|
|
NOTE: Jungletree-placing code is currently duplicated in the engine
|
|
and in games that have saplings; both are deprecated but not
|
|
replaced yet
|
|
--]]
|
|
if bad then
|
|
error("Deprecated use of default.grow_jungle_tree")
|
|
end
|
|
|
|
local x, y, z = pos.x, pos.y, pos.z
|
|
local height = random(8, 12)
|
|
local c_air = minetest.get_content_id("air")
|
|
local c_ignore = minetest.get_content_id("ignore")
|
|
local c_jungletree = minetest.get_content_id("default:jungletree")
|
|
local c_jungleleaves = minetest.get_content_id("default:jungleleaves")
|
|
|
|
local vm = minetest.get_voxel_manip()
|
|
local minp, maxp = vm:read_from_map(
|
|
{x = pos.x - 3, y = pos.y - 1, z = pos.z - 3},
|
|
{x = pos.x + 3, y = pos.y + height + 1, z = pos.z + 3}
|
|
)
|
|
local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
|
|
local data = vm:get_data()
|
|
|
|
add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves, height, 3, 30, false)
|
|
|
|
-- Roots
|
|
for z_dist = -1, 1 do
|
|
local vi_1 = a:index(x - 1, y - 1, z + z_dist)
|
|
local vi_2 = a:index(x - 1, y, z + z_dist)
|
|
for x_dist = -1, 1 do
|
|
if random(1, 3) >= 2 then
|
|
if data[vi_1] == c_air or data[vi_1] == c_ignore then
|
|
data[vi_1] = c_jungletree
|
|
elseif data[vi_2] == c_air or data[vi_2] == c_ignore then
|
|
data[vi_2] = c_jungletree
|
|
end
|
|
end
|
|
vi_1 = vi_1 + 1
|
|
vi_2 = vi_2 + 1
|
|
end
|
|
end
|
|
|
|
vm:set_data(data)
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
end
|
|
|
|
|
|
-- Pine tree from mg mapgen mod, design by sfan5, pointy top added by paramat
|
|
|
|
local function add_pine_needles(data, vi, c_air, c_ignore, c_snow, c_pine_needles)
|
|
local node_id = data[vi]
|
|
if node_id == c_air or node_id == c_ignore or node_id == c_snow then
|
|
data[vi] = c_pine_needles
|
|
end
|
|
end
|
|
|
|
local function add_snow(data, vi, c_air, c_ignore, c_snow)
|
|
local node_id = data[vi]
|
|
if node_id == c_air or node_id == c_ignore then
|
|
data[vi] = c_snow
|
|
end
|
|
end
|
|
|
|
function default.grow_pine_tree(pos)
|
|
local x, y, z = pos.x, pos.y, pos.z
|
|
local maxy = y + random(9, 13) -- Trunk top
|
|
|
|
local c_air = minetest.get_content_id("air")
|
|
local c_ignore = minetest.get_content_id("ignore")
|
|
local c_pine_tree = minetest.get_content_id("default:pine_tree")
|
|
local c_pine_needles = minetest.get_content_id("default:pine_needles")
|
|
local c_snow = minetest.get_content_id("default:snow")
|
|
local c_snowblock = minetest.get_content_id("default:snowblock")
|
|
local c_dirtsnow = minetest.get_content_id("default:dirt_with_snow")
|
|
|
|
local vm = minetest.get_voxel_manip()
|
|
local minp, maxp = vm:read_from_map(
|
|
{x = x - 3, y = y - 1, z = z - 3},
|
|
{x = x + 3, y = maxy + 3, z = z + 3}
|
|
)
|
|
local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
|
|
local data = vm:get_data()
|
|
|
|
-- Scan for snow nodes near sapling to enable snow on branches
|
|
local snow = false
|
|
for yy = y - 1, y + 1 do
|
|
for zz = z - 1, z + 1 do
|
|
local vi = a:index(x - 1, yy, zz)
|
|
for xx = x - 1, x + 1 do
|
|
local nodid = data[vi]
|
|
if nodid == c_snow or nodid == c_snowblock or nodid == c_dirtsnow then
|
|
snow = true
|
|
end
|
|
vi = vi + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Upper branches layer
|
|
local dev = 3
|
|
for yy = maxy - 1, maxy + 1 do
|
|
for zz = z - dev, z + dev do
|
|
local vi = a:index(x - dev, yy, zz)
|
|
local via = a:index(x - dev, yy + 1, zz)
|
|
for xx = x - dev, x + dev do
|
|
if random() < 0.95 - dev * 0.05 then
|
|
add_pine_needles(data, vi, c_air, c_ignore, c_snow,
|
|
c_pine_needles)
|
|
if snow then
|
|
add_snow(data, via, c_air, c_ignore, c_snow)
|
|
end
|
|
end
|
|
vi = vi + 1
|
|
via = via + 1
|
|
end
|
|
end
|
|
dev = dev - 1
|
|
end
|
|
|
|
-- Centre top nodes
|
|
add_pine_needles(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow,
|
|
c_pine_needles)
|
|
add_pine_needles(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow,
|
|
c_pine_needles) -- Paramat added a pointy top node
|
|
if snow then
|
|
add_snow(data, a:index(x, maxy + 3, z), c_air, c_ignore, c_snow)
|
|
end
|
|
|
|
-- Lower branches layer
|
|
local my = 0
|
|
for i = 1, 20 do -- Random 2x2 squares of needles
|
|
local xi = x + random(-3, 2)
|
|
local yy = maxy + random(-6, -5)
|
|
local zi = z + random(-3, 2)
|
|
if yy > my then
|
|
my = yy
|
|
end
|
|
for zz = zi, zi+1 do
|
|
local vi = a:index(xi, yy, zz)
|
|
local via = a:index(xi, yy + 1, zz)
|
|
for xx = xi, xi + 1 do
|
|
add_pine_needles(data, vi, c_air, c_ignore, c_snow,
|
|
c_pine_needles)
|
|
if snow then
|
|
add_snow(data, via, c_air, c_ignore, c_snow)
|
|
end
|
|
vi = vi + 1
|
|
via = via + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
local dev = 2
|
|
for yy = my + 1, my + 2 do
|
|
for zz = z - dev, z + dev do
|
|
local vi = a:index(x - dev, yy, zz)
|
|
local via = a:index(x - dev, yy + 1, zz)
|
|
for xx = x - dev, x + dev do
|
|
if random() < 0.95 - dev * 0.05 then
|
|
add_pine_needles(data, vi, c_air, c_ignore, c_snow,
|
|
c_pine_needles)
|
|
if snow then
|
|
add_snow(data, via, c_air, c_ignore, c_snow)
|
|
end
|
|
end
|
|
vi = vi + 1
|
|
via = via + 1
|
|
end
|
|
end
|
|
dev = dev - 1
|
|
end
|
|
|
|
-- Trunk
|
|
data[a:index(x, y, z)] = c_pine_tree -- Force-place lowest trunk node to replace sapling
|
|
for yy = y + 1, maxy do
|
|
local vi = a:index(x, yy, z)
|
|
local node_id = data[vi]
|
|
if node_id == c_air or node_id == c_ignore or
|
|
node_id == c_pine_needles or node_id == c_snow then
|
|
data[vi] = c_pine_tree
|
|
end
|
|
end
|
|
|
|
vm:set_data(data)
|
|
vm:write_to_map()
|
|
vm:update_map()
|
|
end
|
|
|
|
|
|
-- New apple tree
|
|
|
|
function default.grow_new_apple_tree(pos)
|
|
local path = minetest.get_modpath("default") .. "/schematics/apple_tree_from_sapling.mts"
|
|
minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
|
|
path, 0, nil, false)
|
|
end
|
|
|
|
|
|
-- New jungle tree
|
|
|
|
function default.grow_new_jungle_tree(pos)
|
|
local path = minetest.get_modpath("default") .. "/schematics/jungle_tree_from_sapling.mts"
|
|
minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
|
|
path, 0, nil, false)
|
|
end
|
|
|
|
|
|
-- New pine tree
|
|
|
|
function default.grow_new_pine_tree(pos)
|
|
local path = minetest.get_modpath("default") .. "/schematics/pine_tree_from_sapling.mts"
|
|
minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
|
|
path, 0, nil, false)
|
|
end
|
|
|
|
|
|
-- New acacia tree
|
|
|
|
function default.grow_new_acacia_tree(pos)
|
|
local path = minetest.get_modpath("default") .. "/schematics/acacia_tree_from_sapling.mts"
|
|
minetest.place_schematic({x = pos.x - 4, y = pos.y - 1, z = pos.z - 4},
|
|
path, random, nil, false)
|
|
end
|
|
|
|
-- New aspen tree
|
|
|
|
function default.grow_new_aspen_tree(pos)
|
|
local path = minetest.get_modpath("default") .. "/schematics/aspen_tree_from_sapling.mts"
|
|
minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
|
|
path, 0, nil, false)
|
|
end
|