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