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