New normalmaps for some nodes
[oweals/minetest_game.git] / mods / default / trees.lua
1 --
2 -- Grow trees from saplings
3 --
4
5 -- 'Can grow' function
6
7 local random = math.random
8
9 local function can_grow(pos)
10         local node_under = minetest.get_node_or_nil({x = pos.x, y = pos.y - 1, z = pos.z})
11         if not node_under then
12                 return false
13         end
14         local name_under = node_under.name
15         local is_soil = minetest.get_item_group(name_under, "soil")
16         if is_soil == 0 then
17                 return false
18         end
19         return true
20 end
21
22
23 -- Sapling ABM
24
25 minetest.register_abm({
26         nodenames = {"default:sapling", "default:junglesapling",
27                 "default:pine_sapling", "default:acacia_sapling"},
28         interval = 10,
29         chance = 50,
30         action = function(pos, node)
31                 if not can_grow(pos) then
32                         return
33                 end
34
35                 local mapgen = minetest.get_mapgen_params().mgname
36                 if node.name == "default:sapling" then
37                         minetest.log("action", "A sapling grows into a tree at "..
38                                 minetest.pos_to_string(pos))
39                         if mapgen == "v6" then
40                                 default.grow_tree(pos, random(1, 4) == 1)
41                         else
42                                 default.grow_new_apple_tree(pos)
43                         end
44                 elseif node.name == "default:junglesapling" then
45                         minetest.log("action", "A jungle sapling grows into a tree at "..
46                                 minetest.pos_to_string(pos))
47                         if mapgen == "v6" then
48                                 default.grow_jungle_tree(pos)
49                         else
50                                 default.grow_new_jungle_tree(pos)
51                         end
52                 elseif node.name == "default:pine_sapling" then
53                         minetest.log("action", "A pine sapling grows into a tree at "..
54                                 minetest.pos_to_string(pos))
55                         if mapgen == "v6" then
56                                 default.grow_pine_tree(pos)
57                         else
58                                 default.grow_new_pine_tree(pos)
59                         end
60                 elseif node.name == "default:acacia_sapling" then
61                         minetest.log("action", "An acacia sapling grows into a tree at "..
62                                 minetest.pos_to_string(pos))
63                         default.grow_new_acacia_tree(pos)
64                 end
65         end
66 })
67
68
69 --
70 -- Tree generation
71 --
72
73 -- Apple tree and jungle tree trunk and leaves function
74
75 local function add_trunk_and_leaves(data, a, pos, tree_cid, leaves_cid,
76                 height, size, iters, is_apple_tree)
77         local x, y, z = pos.x, pos.y, pos.z
78         local c_air = minetest.get_content_id("air")
79         local c_ignore = minetest.get_content_id("ignore")
80         local c_apple = minetest.get_content_id("default:apple")
81
82         -- Trunk
83         data[a:index(x, y, z)] = tree_cid -- Force-place lowest trunk node to replace sapling
84         for yy = y + 1, y + height - 1 do
85                 local vi = a:index(x, yy, z)
86                 local node_id = data[vi]
87                 if node_id == c_air or node_id == c_ignore or node_id == leaves_cid then
88                         data[vi] = tree_cid
89                 end
90         end
91
92         -- Force leaves near the trunk
93         for z_dist = -1, 1 do
94         for y_dist = -size, 1 do
95                 local vi = a:index(x - 1, y + height + y_dist, z + z_dist)
96                 for x_dist = -1, 1 do
97                         if data[vi] == c_air or data[vi] == c_ignore then
98                                 if is_apple_tree and random(1, 8) == 1 then
99                                         data[vi] = c_apple
100                                 else
101                                         data[vi] = leaves_cid
102                                 end
103                         end
104                         vi = vi + 1
105                 end
106         end
107         end
108
109         -- Randomly add leaves in 2x2x2 clusters.
110         for i = 1, iters do
111                 local clust_x = x + random(-size, size - 1)
112                 local clust_y = y + height + random(-size, 0)
113                 local clust_z = z + random(-size, size - 1)
114
115                 for xi = 0, 1 do
116                 for yi = 0, 1 do
117                 for zi = 0, 1 do
118                         local vi = a:index(clust_x + xi, clust_y + yi, clust_z + zi)
119                         if data[vi] == c_air or data[vi] == c_ignore then
120                                 if is_apple_tree and random(1, 8) == 1 then
121                                         data[vi] = c_apple
122                                 else
123                                         data[vi] = leaves_cid
124                                 end
125                         end
126                 end
127                 end
128                 end
129         end
130 end
131
132
133 -- Apple tree
134
135 function default.grow_tree(pos, is_apple_tree, bad)
136         --[[
137                 NOTE: Tree-placing code is currently duplicated in the engine
138                 and in games that have saplings; both are deprecated but not
139                 replaced yet
140         --]]
141         if bad then
142                 error("Deprecated use of default.grow_tree")
143         end
144
145         local x, y, z = pos.x, pos.y, pos.z
146         local height = random(4, 5)
147         local c_tree = minetest.get_content_id("default:tree")
148         local c_leaves = minetest.get_content_id("default:leaves")
149
150         local vm = minetest.get_voxel_manip()
151         local minp, maxp = vm:read_from_map(
152                 {x = pos.x - 2, y = pos.y, z = pos.z - 2},
153                 {x = pos.x + 2, y = pos.y + height + 1, z = pos.z + 2}
154         )
155         local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
156         local data = vm:get_data()
157
158         add_trunk_and_leaves(data, a, pos, c_tree, c_leaves, height, 2, 8, is_apple_tree)
159
160         vm:set_data(data)
161         vm:write_to_map()
162         vm:update_map()
163 end
164
165
166 -- Jungle tree
167
168 function default.grow_jungle_tree(pos, bad)
169         --[[
170                 NOTE: Jungletree-placing code is currently duplicated in the engine
171                 and in games that have saplings; both are deprecated but not
172                 replaced yet
173         --]]
174         if bad then
175                 error("Deprecated use of default.grow_jungle_tree")
176         end
177
178         local x, y, z = pos.x, pos.y, pos.z
179         local height = random(8, 12)
180         local c_air = minetest.get_content_id("air")
181         local c_ignore = minetest.get_content_id("ignore")
182         local c_jungletree = minetest.get_content_id("default:jungletree")
183         local c_jungleleaves = minetest.get_content_id("default:jungleleaves")
184
185         local vm = minetest.get_voxel_manip()
186         local minp, maxp = vm:read_from_map(
187                 {x = pos.x - 3, y = pos.y - 1, z = pos.z - 3},
188                 {x = pos.x + 3, y = pos.y + height + 1, z = pos.z + 3}
189         )
190         local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
191         local data = vm:get_data()
192
193         add_trunk_and_leaves(data, a, pos, c_jungletree, c_jungleleaves, height, 3, 30, false)
194
195         -- Roots
196         for z_dist = -1, 1 do
197                 local vi_1 = a:index(x - 1, y - 1, z + z_dist)
198                 local vi_2 = a:index(x - 1, y, z + z_dist)
199                 for x_dist = -1, 1 do
200                         if random(1, 3) >= 2 then
201                                 if data[vi_1] == c_air or data[vi_1] == c_ignore then
202                                         data[vi_1] = c_jungletree
203                                 elseif data[vi_2] == c_air or data[vi_2] == c_ignore then
204                                         data[vi_2] = c_jungletree
205                                 end
206                         end
207                         vi_1 = vi_1 + 1
208                         vi_2 = vi_2 + 1
209                 end
210         end
211
212         vm:set_data(data)
213         vm:write_to_map()
214         vm:update_map()
215 end
216
217
218 -- Pine tree from mg mapgen mod, design by sfan5, pointy top added by paramat
219
220 local function add_pine_needles(data, vi, c_air, c_ignore, c_snow, c_pine_needles)
221         local node_id = data[vi]
222         if node_id == c_air or node_id == c_ignore or node_id == c_snow then
223                 data[vi] = c_pine_needles
224         end
225 end
226
227 local function add_snow(data, vi, c_air, c_ignore, c_snow)
228         local node_id = data[vi]
229         if node_id == c_air or node_id == c_ignore then
230                 data[vi] = c_snow
231         end
232 end
233
234 function default.grow_pine_tree(pos)
235         local x, y, z = pos.x, pos.y, pos.z
236         local maxy = y + random(9, 13) -- Trunk top
237
238         local c_air = minetest.get_content_id("air")
239         local c_ignore = minetest.get_content_id("ignore")
240         local c_pine_tree = minetest.get_content_id("default:pine_tree")
241         local c_pine_needles  = minetest.get_content_id("default:pine_needles")
242         local c_snow = minetest.get_content_id("default:snow")
243         local c_snowblock = minetest.get_content_id("default:snowblock")
244         local c_dirtsnow = minetest.get_content_id("default:dirt_with_snow")
245
246         local vm = minetest.get_voxel_manip()
247         local minp, maxp = vm:read_from_map(
248                 {x = x - 3, y = y - 1, z = z - 3},
249                 {x = x + 3, y = maxy + 3, z = z + 3}
250         )
251         local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp})
252         local data = vm:get_data()
253
254         -- Scan for snow nodes near sapling to enable snow on branches
255         local snow = false
256         for yy = y - 1, y + 1 do
257         for zz = z - 1, z + 1 do
258                 local vi  = a:index(x - 1, yy, zz)
259                 for xx = x - 1, x + 1 do
260                         local nodid = data[vi]
261                         if nodid == c_snow or nodid == c_snowblock or nodid == c_dirtsnow then
262                                 snow = true
263                         end
264                         vi  = vi + 1
265                 end
266         end
267         end
268
269         -- Upper branches layer
270         local dev = 3
271         for yy = maxy - 1, maxy + 1 do
272                 for zz = z - dev, z + dev do
273                         local vi = a:index(x - dev, yy, zz)
274                         local via = a:index(x - dev, yy + 1, zz)
275                         for xx = x - dev, x + dev do
276                                 if random() < 0.95 - dev * 0.05 then
277                                         add_pine_needles(data, vi, c_air, c_ignore, c_snow,
278                                                 c_pine_needles)
279                                         if snow then
280                                                 add_snow(data, via, c_air, c_ignore, c_snow)
281                                         end
282                                 end
283                                 vi  = vi + 1
284                                 via = via + 1
285                         end
286                 end
287                 dev = dev - 1
288         end
289
290         -- Centre top nodes
291         add_pine_needles(data, a:index(x, maxy + 1, z), c_air, c_ignore, c_snow,
292                 c_pine_needles)
293         add_pine_needles(data, a:index(x, maxy + 2, z), c_air, c_ignore, c_snow,
294                 c_pine_needles) -- Paramat added a pointy top node
295         if snow then
296                 add_snow(data, a:index(x, maxy + 3, z), c_air, c_ignore, c_snow)
297         end
298
299         -- Lower branches layer
300         local my = 0
301         for i = 1, 20 do -- Random 2x2 squares of needles
302                 local xi = x + random(-3, 2)
303                 local yy = maxy + random(-6, -5)
304                 local zi = z + random(-3, 2)
305                 if yy > my then
306                         my = yy
307                 end
308                 for zz = zi, zi+1 do
309                         local vi = a:index(xi, yy, zz)
310                         local via = a:index(xi, yy + 1, zz)
311                         for xx = xi, xi + 1 do
312                                 add_pine_needles(data, vi, c_air, c_ignore, c_snow,
313                                         c_pine_needles)
314                                 if snow then
315                                         add_snow(data, via, c_air, c_ignore, c_snow)
316                                 end
317                                 vi  = vi + 1
318                                 via = via + 1
319                         end
320                 end
321         end
322
323         local dev = 2
324         for yy = my + 1, my + 2 do
325                 for zz = z - dev, z + dev do
326                         local vi = a:index(x - dev, yy, zz)
327                         local via = a:index(x - dev, yy + 1, zz)
328                         for xx = x - dev, x + dev do
329                                 if random() < 0.95 - dev * 0.05 then
330                                         add_pine_needles(data, vi, c_air, c_ignore, c_snow,
331                                                 c_pine_needles)
332                                         if snow then
333                                                 add_snow(data, via, c_air, c_ignore, c_snow)
334                                         end
335                                 end
336                                 vi  = vi + 1
337                                 via = via + 1
338                         end
339                 end
340                 dev = dev - 1
341         end
342
343         -- Trunk
344         data[a:index(x, y, z)] = c_pine_tree -- Force-place lowest trunk node to replace sapling
345         for yy = y + 1, maxy do
346                 local vi = a:index(x, yy, z)
347                 local node_id = data[vi]
348                 if node_id == c_air or node_id == c_ignore or
349                                 node_id == c_pine_needles or node_id == c_snow then
350                         data[vi] = c_pine_tree
351                 end
352         end
353
354         vm:set_data(data)
355         vm:write_to_map()
356         vm:update_map()
357 end
358
359
360 -- New apple tree
361
362 function default.grow_new_apple_tree(pos)
363         local path = minetest.get_modpath("default") .. "/schematics/apple_tree.mts"
364         minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
365                 path, 0, nil, false)
366 end
367
368
369 -- New jungle tree
370
371 function default.grow_new_jungle_tree(pos)
372         local path = minetest.get_modpath("default") .. "/schematics/jungle_tree.mts"
373         minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
374                 path, 0, nil, false)
375 end
376
377
378 -- New pine tree
379
380 function default.grow_new_pine_tree(pos)
381         local path = minetest.get_modpath("default") .. "/schematics/pine_tree.mts"
382         minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2},
383                 path, 0, nil, false)
384 end
385
386
387 -- New acacia tree
388
389 function default.grow_new_acacia_tree(pos)
390         local path = minetest.get_modpath("default") .. "/schematics/acacia_tree.mts"
391         minetest.place_schematic({x = pos.x - 4, y = pos.y - 1, z = pos.z - 4},
392                 path, random, nil, false)
393 end