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