5cab6eab9ba4e97108d838f9dd884f5d6ee8580d
[oweals/minetest.git] / src / mapgen.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapgen.h"
21 #include "voxel.h"
22 #include "noise.h"
23 #include "mapblock.h"
24 #include "map.h"
25 //#include "serverobject.h"
26 #include "content_sao.h"
27 #include "nodedef.h"
28 #include "content_mapnode.h" // For content_mapnode_get_new_name
29 #include "voxelalgorithms.h"
30 #include "profiler.h"
31 #include "main.h" // For g_profiler
32
33 namespace mapgen
34 {
35
36 /*
37         Some helper functions for the map generator
38 */
39
40 #if 0
41 static s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
42 {
43         v3s16 em = vmanip.m_area.getExtent();
44         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
45         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
46         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
47         s16 y;
48         for(y=y_nodes_max; y>=y_nodes_min; y--)
49         {
50                 MapNode &n = vmanip.m_data[i];
51                 if(content_walkable(n.d))
52                         break;
53
54                 vmanip.m_area.add_y(em, i, -1);
55         }
56         if(y >= y_nodes_min)
57                 return y;
58         else
59                 return y_nodes_min;
60 }
61
62 static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
63 {
64         v3s16 em = vmanip.m_area.getExtent();
65         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
66         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
67         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
68         s16 y;
69         for(y=y_nodes_max; y>=y_nodes_min; y--)
70         {
71                 MapNode &n = vmanip.m_data[i];
72                 if(content_walkable(n.d)
73                                 && n.getContent() != LEGN(ndef, "CONTENT_TREE")
74                                 && n.getContent() != LEGN(ndef, "CONTENT_LEAVES"))
75                         break;
76
77                 vmanip.m_area.add_y(em, i, -1);
78         }
79         if(y >= y_nodes_min)
80                 return y;
81         else
82                 return y_nodes_min;
83 }
84 #endif
85
86 void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0,
87                 bool is_apple_tree, INodeDefManager *ndef)
88 {
89         MapNode treenode(LEGN(ndef, "CONTENT_TREE"));
90         MapNode leavesnode(LEGN(ndef, "CONTENT_LEAVES"));
91         MapNode applenode(LEGN(ndef, "CONTENT_APPLE"));
92         
93         s16 trunk_h = myrand_range(4, 5);
94         v3s16 p1 = p0;
95         for(s16 ii=0; ii<trunk_h; ii++)
96         {
97                 if(vmanip.m_area.contains(p1))
98                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
99                 p1.Y++;
100         }
101
102         // p1 is now the last piece of the trunk
103         p1.Y -= 1;
104
105         VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
106         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
107         Buffer<u8> leaves_d(leaves_a.getVolume());
108         for(s32 i=0; i<leaves_a.getVolume(); i++)
109                 leaves_d[i] = 0;
110
111         // Force leaves at near the end of the trunk
112         {
113                 s16 d = 1;
114                 for(s16 z=-d; z<=d; z++)
115                 for(s16 y=-d; y<=d; y++)
116                 for(s16 x=-d; x<=d; x++)
117                 {
118                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
119                 }
120         }
121
122         // Add leaves randomly
123         for(u32 iii=0; iii<7; iii++)
124         {
125                 s16 d = 1;
126
127                 v3s16 p(
128                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
129                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
130                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
131                 );
132
133                 for(s16 z=0; z<=d; z++)
134                 for(s16 y=0; y<=d; y++)
135                 for(s16 x=0; x<=d; x++)
136                 {
137                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
138                 }
139         }
140
141         // Blit leaves to vmanip
142         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
143         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
144         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
145         {
146                 v3s16 p(x,y,z);
147                 p += p1;
148                 if(vmanip.m_area.contains(p) == false)
149                         continue;
150                 u32 vi = vmanip.m_area.index(p);
151                 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
152                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
153                         continue;
154                 u32 i = leaves_a.index(x,y,z);
155                 if(leaves_d[i] == 1) {
156                         bool is_apple = myrand_range(0,99) < 10;
157                         if(is_apple_tree && is_apple) {
158                                 vmanip.m_data[vi] = applenode;
159                         } else {
160                                 vmanip.m_data[vi] = leavesnode;
161                         }
162                 }
163         }
164 }
165
166 static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
167                 INodeDefManager *ndef)
168 {
169         MapNode treenode(LEGN(ndef, "CONTENT_JUNGLETREE"));
170         MapNode leavesnode(LEGN(ndef, "CONTENT_LEAVES"));
171
172         for(s16 x=-1; x<=1; x++)
173         for(s16 z=-1; z<=1; z++)
174         {
175                 if(myrand_range(0, 2) == 0)
176                         continue;
177                 v3s16 p1 = p0 + v3s16(x,0,z);
178                 v3s16 p2 = p0 + v3s16(x,-1,z);
179                 if(vmanip.m_area.contains(p2)
180                                 && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR)
181                         vmanip.m_data[vmanip.m_area.index(p2)] = treenode;
182                 else if(vmanip.m_area.contains(p1))
183                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
184         }
185
186         s16 trunk_h = myrand_range(8, 12);
187         v3s16 p1 = p0;
188         for(s16 ii=0; ii<trunk_h; ii++)
189         {
190                 if(vmanip.m_area.contains(p1))
191                         vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
192                 p1.Y++;
193         }
194
195         // p1 is now the last piece of the trunk
196         p1.Y -= 1;
197
198         VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
199         //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
200         Buffer<u8> leaves_d(leaves_a.getVolume());
201         for(s32 i=0; i<leaves_a.getVolume(); i++)
202                 leaves_d[i] = 0;
203
204         // Force leaves at near the end of the trunk
205         {
206                 s16 d = 1;
207                 for(s16 z=-d; z<=d; z++)
208                 for(s16 y=-d; y<=d; y++)
209                 for(s16 x=-d; x<=d; x++)
210                 {
211                         leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
212                 }
213         }
214
215         // Add leaves randomly
216         for(u32 iii=0; iii<30; iii++)
217         {
218                 s16 d = 1;
219
220                 v3s16 p(
221                         myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
222                         myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
223                         myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
224                 );
225
226                 for(s16 z=0; z<=d; z++)
227                 for(s16 y=0; y<=d; y++)
228                 for(s16 x=0; x<=d; x++)
229                 {
230                         leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
231                 }
232         }
233
234         // Blit leaves to vmanip
235         for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
236         for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
237         for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
238         {
239                 v3s16 p(x,y,z);
240                 p += p1;
241                 if(vmanip.m_area.contains(p) == false)
242                         continue;
243                 u32 vi = vmanip.m_area.index(p);
244                 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
245                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
246                         continue;
247                 u32 i = leaves_a.index(x,y,z);
248                 if(leaves_d[i] == 1)
249                         vmanip.m_data[vi] = leavesnode;
250         }
251 }
252
253 void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
254                 INodeDefManager *ndef)
255 {
256         MapNode papyrusnode(LEGN(ndef, "CONTENT_PAPYRUS"));
257
258         s16 trunk_h = myrand_range(2, 3);
259         v3s16 p1 = p0;
260         for(s16 ii=0; ii<trunk_h; ii++)
261         {
262                 if(vmanip.m_area.contains(p1))
263                         vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
264                 p1.Y++;
265         }
266 }
267
268 void make_cactus(VoxelManipulator &vmanip, v3s16 p0,
269                 INodeDefManager *ndef)
270 {
271         MapNode cactusnode(LEGN(ndef, "CONTENT_CACTUS"));
272
273         s16 trunk_h = 3;
274         v3s16 p1 = p0;
275         for(s16 ii=0; ii<trunk_h; ii++)
276         {
277                 if(vmanip.m_area.contains(p1))
278                         vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
279                 p1.Y++;
280         }
281 }
282
283 #if 0
284 static void make_randomstone(VoxelManipulator &vmanip, v3s16 p0)
285 {
286         MapNode stonenode(LEGN(ndef, "CONTENT_STONE"));
287
288         s16 size = myrand_range(3, 6);
289         
290         VoxelArea stone_a(v3s16(-2,0,-2), v3s16(2,size,2));
291         Buffer<u8> stone_d(stone_a.getVolume());
292         for(s32 i=0; i<stone_a.getVolume(); i++)
293                 stone_d[i] = 0;
294
295         // Force stone at bottom to make it usually touch the ground
296         {
297                 for(s16 z=0; z<=0; z++)
298                 for(s16 y=0; y<=0; y++)
299                 for(s16 x=0; x<=0; x++)
300                 {
301                         stone_d[stone_a.index(v3s16(x,y,z))] = 1;
302                 }
303         }
304
305         // Generate from perlin noise
306         for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
307         for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
308         for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
309         {
310                 double d = noise3d_perlin((float)x/3.,(float)z/3.,(float)y/3.,
311                                 p0.Z*4243+p0.Y*34+p0.X, 2, 0.5);
312                 if(z == stone_a.MinEdge.Z || z == stone_a.MaxEdge.Z)
313                         d -= 0.3;
314                 if(/*y == stone_a.MinEdge.Y ||*/ y == stone_a.MaxEdge.Y)
315                         d -= 0.3;
316                 if(x == stone_a.MinEdge.X || x == stone_a.MaxEdge.X)
317                         d -= 0.3;
318                 if(d > 0.0)
319                 {
320                         u32 vi = stone_a.index(v3s16(x,y,z));
321                         stone_d[vi] = 1;
322                 }
323         }
324
325         /*// Add stone randomly
326         for(u32 iii=0; iii<7; iii++)
327         {
328                 s16 d = 1;
329
330                 v3s16 p(
331                         myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
332                         myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
333                         myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
334                 );
335
336                 for(s16 z=0; z<=d; z++)
337                 for(s16 y=0; y<=d; y++)
338                 for(s16 x=0; x<=d; x++)
339                 {
340                         stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
341                 }
342         }*/
343
344         // Blit stone to vmanip
345         for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
346         for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
347         for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
348         {
349                 v3s16 p(x,y,z);
350                 p += p0;
351                 if(vmanip.m_area.contains(p) == false)
352                         continue;
353                 u32 vi = vmanip.m_area.index(p);
354                 if(vmanip.m_data[vi].getContent() != CONTENT_AIR
355                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
356                         continue;
357                 u32 i = stone_a.index(x,y,z);
358                 if(stone_d[i] == 1)
359                         vmanip.m_data[vi] = stonenode;
360         }
361 }
362 #endif
363
364 #if 0
365 static void make_largestone(VoxelManipulator &vmanip, v3s16 p0)
366 {
367         MapNode stonenode(LEGN(ndef, "CONTENT_STONE"));
368
369         s16 size = myrand_range(8, 16);
370         
371         VoxelArea stone_a(v3s16(-size/2,0,-size/2), v3s16(size/2,size,size/2));
372         Buffer<u8> stone_d(stone_a.getVolume());
373         for(s32 i=0; i<stone_a.getVolume(); i++)
374                 stone_d[i] = 0;
375
376         // Force stone at bottom to make it usually touch the ground
377         {
378                 for(s16 z=0; z<=0; z++)
379                 for(s16 y=0; y<=0; y++)
380                 for(s16 x=0; x<=0; x++)
381                 {
382                         stone_d[stone_a.index(v3s16(x,y,z))] = 1;
383                 }
384         }
385
386         // Generate from perlin noise
387         for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
388         for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
389         for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
390         {
391                 double d = 1.0;
392                 d += noise3d_perlin((float)x/10.,(float)z/10.,(float)y/10.,
393                                 p0.Z*5123+p0.Y*2439+p0.X, 2, 0.5);
394                 double mid_z = (stone_a.MaxEdge.Z+stone_a.MinEdge.Z)/2;
395                 double mid_x = (stone_a.MaxEdge.X+stone_a.MinEdge.X)/2;
396                 double mid_y = (stone_a.MaxEdge.Y+stone_a.MinEdge.Y)/2;
397                 double dz = (double)z-mid_z;
398                 double dx = (double)x-mid_x;
399                 double dy = MYMAX(0, (double)y-mid_y);
400                 double r = sqrt(dz*dz+dx*dx+dy*dy);
401                 d /= (2*r/size)*2 + 0.01;
402                 if(d > 1.0)
403                 {
404                         u32 vi = stone_a.index(v3s16(x,y,z));
405                         stone_d[vi] = 1;
406                 }
407         }
408
409         /*// Add stone randomly
410         for(u32 iii=0; iii<7; iii++)
411         {
412                 s16 d = 1;
413
414                 v3s16 p(
415                         myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
416                         myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
417                         myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
418                 );
419
420                 for(s16 z=0; z<=d; z++)
421                 for(s16 y=0; y<=d; y++)
422                 for(s16 x=0; x<=d; x++)
423                 {
424                         stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
425                 }
426         }*/
427
428         // Blit stone to vmanip
429         for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
430         for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
431         for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
432         {
433                 v3s16 p(x,y,z);
434                 p += p0;
435                 if(vmanip.m_area.contains(p) == false)
436                         continue;
437                 u32 vi = vmanip.m_area.index(p);
438                 /*if(vmanip.m_data[vi].getContent() != CONTENT_AIR
439                                 && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
440                         continue;*/
441                 u32 i = stone_a.index(x,y,z);
442                 if(stone_d[i] == 1)
443                         vmanip.m_data[vi] = stonenode;
444         }
445 }
446 #endif
447
448 /*
449         Dungeon making routines
450 */
451
452 #define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
453 #define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
454 #define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
455                 VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
456
457 static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace,
458                 INodeDefManager *ndef)
459 {
460         // Make +-X walls
461         for(s16 z=0; z<roomsize.Z; z++)
462         for(s16 y=0; y<roomsize.Y; y++)
463         {
464                 {
465                         v3s16 p = roomplace + v3s16(0,y,z);
466                         if(vmanip.m_area.contains(p) == false)
467                                 continue;
468                         u32 vi = vmanip.m_area.index(p);
469                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
470                                 continue;
471                         vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_COBBLE"));
472                 }
473                 {
474                         v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
475                         if(vmanip.m_area.contains(p) == false)
476                                 continue;
477                         u32 vi = vmanip.m_area.index(p);
478                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
479                                 continue;
480                         vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_COBBLE"));
481                 }
482         }
483         
484         // Make +-Z walls
485         for(s16 x=0; x<roomsize.X; x++)
486         for(s16 y=0; y<roomsize.Y; y++)
487         {
488                 {
489                         v3s16 p = roomplace + v3s16(x,y,0);
490                         if(vmanip.m_area.contains(p) == false)
491                                 continue;
492                         u32 vi = vmanip.m_area.index(p);
493                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
494                                 continue;
495                         vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_COBBLE"));
496                 }
497                 {
498                         v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
499                         if(vmanip.m_area.contains(p) == false)
500                                 continue;
501                         u32 vi = vmanip.m_area.index(p);
502                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
503                                 continue;
504                         vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_COBBLE"));
505                 }
506         }
507         
508         // Make +-Y walls (floor and ceiling)
509         for(s16 z=0; z<roomsize.Z; z++)
510         for(s16 x=0; x<roomsize.X; x++)
511         {
512                 {
513                         v3s16 p = roomplace + v3s16(x,0,z);
514                         if(vmanip.m_area.contains(p) == false)
515                                 continue;
516                         u32 vi = vmanip.m_area.index(p);
517                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
518                                 continue;
519                         vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_COBBLE"));
520                 }
521                 {
522                         v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
523                         if(vmanip.m_area.contains(p) == false)
524                                 continue;
525                         u32 vi = vmanip.m_area.index(p);
526                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
527                                 continue;
528                         vmanip.m_data[vi] = MapNode(LEGN(ndef, "CONTENT_COBBLE"));
529                 }
530         }
531         
532         // Fill with air
533         for(s16 z=1; z<roomsize.Z-1; z++)
534         for(s16 y=1; y<roomsize.Y-1; y++)
535         for(s16 x=1; x<roomsize.X-1; x++)
536         {
537                 v3s16 p = roomplace + v3s16(x,y,z);
538                 if(vmanip.m_area.contains(p) == false)
539                         continue;
540                 u32 vi = vmanip.m_area.index(p);
541                 vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
542                 vmanip.m_data[vi] = MapNode(CONTENT_AIR);
543         }
544 }
545
546 static void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
547                 u8 avoid_flags, MapNode n, u8 or_flags)
548 {
549         for(s16 z=0; z<size.Z; z++)
550         for(s16 y=0; y<size.Y; y++)
551         for(s16 x=0; x<size.X; x++)
552         {
553                 v3s16 p = place + v3s16(x,y,z);
554                 if(vmanip.m_area.contains(p) == false)
555                         continue;
556                 u32 vi = vmanip.m_area.index(p);
557                 if(vmanip.m_flags[vi] & avoid_flags)
558                         continue;
559                 vmanip.m_flags[vi] |= or_flags;
560                 vmanip.m_data[vi] = n;
561         }
562 }
563
564 static void make_hole1(VoxelManipulator &vmanip, v3s16 place,
565                 INodeDefManager *ndef)
566 {
567         make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
568                         VMANIP_FLAG_DUNGEON_INSIDE);
569 }
570
571 static void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
572                 INodeDefManager *ndef)
573 {
574         make_hole1(vmanip, doorplace, ndef);
575         // Place torch (for testing)
576         //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(LEGN(ndef, "CONTENT_TORCH"));
577 }
578
579 static v3s16 rand_ortho_dir(PseudoRandom &random)
580 {
581         if(random.next()%2==0)
582                 return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
583         else
584                 return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
585 }
586
587 static v3s16 turn_xz(v3s16 olddir, int t)
588 {
589         v3s16 dir;
590         if(t == 0)
591         {
592                 // Turn right
593                 dir.X = olddir.Z;
594                 dir.Z = -olddir.X;
595                 dir.Y = olddir.Y;
596         }
597         else
598         {
599                 // Turn left
600                 dir.X = -olddir.Z;
601                 dir.Z = olddir.X;
602                 dir.Y = olddir.Y;
603         }
604         return dir;
605 }
606
607 static v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
608 {
609         int turn = random.range(0,2);
610         v3s16 dir;
611         if(turn == 0)
612         {
613                 // Go straight
614                 dir = olddir;
615         }
616         else if(turn == 1)
617                 // Turn right
618                 dir = turn_xz(olddir, 0);
619         else
620                 // Turn left
621                 dir = turn_xz(olddir, 1);
622         return dir;
623 }
624
625 static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace,
626                 v3s16 doordir, v3s16 &result_place, v3s16 &result_dir,
627                 PseudoRandom &random, INodeDefManager *ndef)
628 {
629         make_hole1(vmanip, doorplace, ndef);
630         v3s16 p0 = doorplace;
631         v3s16 dir = doordir;
632         u32 length;
633         if(random.next()%2)
634                 length = random.range(1,13);
635         else
636                 length = random.range(1,6);
637         length = random.range(1,13);
638         u32 partlength = random.range(1,13);
639         u32 partcount = 0;
640         s16 make_stairs = 0;
641         if(random.next()%2 == 0 && partlength >= 3)
642                 make_stairs = random.next()%2 ? 1 : -1;
643         for(u32 i=0; i<length; i++)
644         {
645                 v3s16 p = p0 + dir;
646                 if(partcount != 0)
647                         p.Y += make_stairs;
648
649                 /*// If already empty
650                 if(vmanip.getNodeNoExNoEmerge(p).getContent()
651                                 == CONTENT_AIR
652                 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
653                                 == CONTENT_AIR)
654                 {
655                 }*/
656
657                 if(vmanip.m_area.contains(p) == true
658                                 && vmanip.m_area.contains(p+v3s16(0,1,0)) == true)
659                 {
660                         if(make_stairs)
661                         {
662                                 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
663                                                 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(LEGN(ndef, "CONTENT_COBBLE")), 0);
664                                 make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
665                                                 VMANIP_FLAG_DUNGEON_INSIDE);
666                                 make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
667                                                 VMANIP_FLAG_DUNGEON_INSIDE);
668                         }
669                         else
670                         {
671                                 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
672                                                 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(LEGN(ndef, "CONTENT_COBBLE")), 0);
673                                 make_hole1(vmanip, p, ndef);
674                                 /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
675                                                 VMANIP_FLAG_DUNGEON_INSIDE);*/
676                         }
677
678                         p0 = p;
679                 }
680                 else
681                 {
682                         // Can't go here, turn away
683                         dir = turn_xz(dir, random.range(0,1));
684                         make_stairs = -make_stairs;
685                         partcount = 0;
686                         partlength = random.range(1,length);
687                         continue;
688                 }
689
690                 partcount++;
691                 if(partcount >= partlength)
692                 {
693                         partcount = 0;
694                         
695                         dir = random_turn(random, dir);
696                         
697                         partlength = random.range(1,length);
698
699                         make_stairs = 0;
700                         if(random.next()%2 == 0 && partlength >= 3)
701                                 make_stairs = random.next()%2 ? 1 : -1;
702                 }
703         }
704         result_place = p0;
705         result_dir = dir;
706 }
707
708 class RoomWalker
709 {
710 public:
711
712         RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
713                         INodeDefManager *ndef):
714                         vmanip(vmanip_),
715                         m_pos(pos),
716                         m_random(random),
717                         m_ndef(ndef)
718         {
719                 randomizeDir();
720         }
721
722         void randomizeDir()
723         {
724                 m_dir = rand_ortho_dir(m_random);
725         }
726
727         void setPos(v3s16 pos)
728         {
729                 m_pos = pos;
730         }
731
732         void setDir(v3s16 dir)
733         {
734                 m_dir = dir;
735         }
736         
737         bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
738         {
739                 for(u32 i=0; i<100; i++)
740                 {
741                         v3s16 p = m_pos + m_dir;
742                         v3s16 p1 = p + v3s16(0,1,0);
743                         if(vmanip.m_area.contains(p) == false
744                                         || vmanip.m_area.contains(p1) == false
745                                         || i % 4 == 0)
746                         {
747                                 randomizeDir();
748                                 continue;
749                         }
750                         if(vmanip.getNodeNoExNoEmerge(p).getContent()
751                                         == LEGN(m_ndef, "CONTENT_COBBLE")
752                         && vmanip.getNodeNoExNoEmerge(p1).getContent()
753                                         == LEGN(m_ndef, "CONTENT_COBBLE"))
754                         {
755                                 // Found wall, this is a good place!
756                                 result_place = p;
757                                 result_dir = m_dir;
758                                 // Randomize next direction
759                                 randomizeDir();
760                                 return true;
761                         }
762                         /*
763                                 Determine where to move next
764                         */
765                         // Jump one up if the actual space is there
766                         if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
767                                         == LEGN(m_ndef, "CONTENT_COBBLE")
768                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
769                                         == LEGN(m_ndef, "CONTENT_AIR")
770                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent()
771                                         == LEGN(m_ndef, "CONTENT_AIR"))
772                                 p += v3s16(0,1,0);
773                         // Jump one down if the actual space is there
774                         if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
775                                         == LEGN(m_ndef, "CONTENT_COBBLE")
776                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
777                                         == LEGN(m_ndef, "CONTENT_AIR")
778                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent()
779                                         == LEGN(m_ndef, "CONTENT_AIR"))
780                                 p += v3s16(0,-1,0);
781                         // Check if walking is now possible
782                         if(vmanip.getNodeNoExNoEmerge(p).getContent()
783                                         != LEGN(m_ndef, "CONTENT_AIR")
784                         || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
785                                         != LEGN(m_ndef, "CONTENT_AIR"))
786                         {
787                                 // Cannot continue walking here
788                                 randomizeDir();
789                                 continue;
790                         }
791                         // Move there
792                         m_pos = p;
793                 }
794                 return false;
795         }
796
797         bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
798                         v3s16 &result_doordir, v3s16 &result_roomplace)
799         {
800                 for(s16 trycount=0; trycount<30; trycount++)
801                 {
802                         v3s16 doorplace;
803                         v3s16 doordir;
804                         bool r = findPlaceForDoor(doorplace, doordir);
805                         if(r == false)
806                                 continue;
807                         v3s16 roomplace;
808                         // X east, Z north, Y up
809 #if 1
810                         if(doordir == v3s16(1,0,0)) // X+
811                                 roomplace = doorplace +
812                                                 v3s16(0,-1,m_random.range(-roomsize.Z+2,-2));
813                         if(doordir == v3s16(-1,0,0)) // X-
814                                 roomplace = doorplace +
815                                                 v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2));
816                         if(doordir == v3s16(0,0,1)) // Z+
817                                 roomplace = doorplace +
818                                                 v3s16(m_random.range(-roomsize.X+2,-2),-1,0);
819                         if(doordir == v3s16(0,0,-1)) // Z-
820                                 roomplace = doorplace +
821                                                 v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1);
822 #endif
823 #if 0
824                         if(doordir == v3s16(1,0,0)) // X+
825                                 roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2);
826                         if(doordir == v3s16(-1,0,0)) // X-
827                                 roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2);
828                         if(doordir == v3s16(0,0,1)) // Z+
829                                 roomplace = doorplace + v3s16(-roomsize.X/2,-1,0);
830                         if(doordir == v3s16(0,0,-1)) // Z-
831                                 roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1);
832 #endif
833                         
834                         // Check fit
835                         bool fits = true;
836                         for(s16 z=1; z<roomsize.Z-1; z++)
837                         for(s16 y=1; y<roomsize.Y-1; y++)
838                         for(s16 x=1; x<roomsize.X-1; x++)
839                         {
840                                 v3s16 p = roomplace + v3s16(x,y,z);
841                                 if(vmanip.m_area.contains(p) == false)
842                                 {
843                                         fits = false;
844                                         break;
845                                 }
846                                 if(vmanip.m_flags[vmanip.m_area.index(p)]
847                                                 & VMANIP_FLAG_DUNGEON_INSIDE)
848                                 {
849                                         fits = false;
850                                         break;
851                                 }
852                         }
853                         if(fits == false)
854                         {
855                                 // Find new place
856                                 continue;
857                         }
858                         result_doorplace = doorplace;
859                         result_doordir = doordir;
860                         result_roomplace = roomplace;
861                         return true;
862                 }
863                 return false;
864         }
865
866 private:
867         VoxelManipulator &vmanip;
868         v3s16 m_pos;
869         v3s16 m_dir;
870         PseudoRandom &m_random;
871         INodeDefManager *m_ndef;
872 };
873
874 static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
875                 INodeDefManager *ndef)
876 {
877         v3s16 areasize = vmanip.m_area.getExtent();
878         v3s16 roomsize;
879         v3s16 roomplace;
880         
881         /*
882                 Find place for first room
883         */
884         bool fits = false;
885         for(u32 i=0; i<100; i++)
886         {
887                 roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
888                 roomplace = vmanip.m_area.MinEdge + v3s16(
889                                 random.range(0,areasize.X-roomsize.X-1),
890                                 random.range(0,areasize.Y-roomsize.Y-1),
891                                 random.range(0,areasize.Z-roomsize.Z-1));
892                 /*
893                         Check that we're not putting the room to an unknown place,
894                         otherwise it might end up floating in the air
895                 */
896                 fits = true;
897                 for(s16 z=1; z<roomsize.Z-1; z++)
898                 for(s16 y=1; y<roomsize.Y-1; y++)
899                 for(s16 x=1; x<roomsize.X-1; x++)
900                 {
901                         v3s16 p = roomplace + v3s16(x,y,z);
902                         u32 vi = vmanip.m_area.index(p);
903                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
904                         {
905                                 fits = false;
906                                 break;
907                         }
908                         if(vmanip.m_data[vi].getContent() == CONTENT_IGNORE)
909                         {
910                                 fits = false;
911                                 break;
912                         }
913                 }
914                 if(fits)
915                         break;
916         }
917         // No place found
918         if(fits == false)
919                 return;
920         
921         /*
922                 Stores the center position of the last room made, so that
923                 a new corridor can be started from the last room instead of
924                 the new room, if chosen so.
925         */
926         v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2);
927         
928         u32 room_count = random.range(2,7);
929         for(u32 i=0; i<room_count; i++)
930         {
931                 // Make a room to the determined place
932                 make_room1(vmanip, roomsize, roomplace, ndef);
933                 
934                 v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2);
935
936                 // Place torch at room center (for testing)
937                 //vmanip.m_data[vmanip.m_area.index(room_center)] = MapNode(LEGN(ndef, "CONTENT_TORCH"));
938
939                 // Quit if last room
940                 if(i == room_count-1)
941                         break;
942                 
943                 // Determine walker start position
944
945                 bool start_in_last_room = (random.range(0,2)!=0);
946                 //bool start_in_last_room = true;
947
948                 v3s16 walker_start_place;
949
950                 if(start_in_last_room)
951                 {
952                         walker_start_place = last_room_center;
953                 }
954                 else
955                 {
956                         walker_start_place = room_center;
957                         // Store center of current room as the last one
958                         last_room_center = room_center;
959                 }
960                 
961                 // Create walker and find a place for a door
962                 RoomWalker walker(vmanip, walker_start_place, random, ndef);
963                 v3s16 doorplace;
964                 v3s16 doordir;
965                 bool r = walker.findPlaceForDoor(doorplace, doordir);
966                 if(r == false)
967                         return;
968                 
969                 if(random.range(0,1)==0)
970                         // Make the door
971                         make_door1(vmanip, doorplace, doordir, ndef);
972                 else
973                         // Don't actually make a door
974                         doorplace -= doordir;
975                 
976                 // Make a random corridor starting from the door
977                 v3s16 corridor_end;
978                 v3s16 corridor_end_dir;
979                 make_corridor(vmanip, doorplace, doordir, corridor_end,
980                                 corridor_end_dir, random, ndef);
981                 
982                 // Find a place for a random sized room
983                 roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
984                 walker.setPos(corridor_end);
985                 walker.setDir(corridor_end_dir);
986                 r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
987                 if(r == false)
988                         return;
989
990                 if(random.range(0,1)==0)
991                         // Make the door
992                         make_door1(vmanip, doorplace, doordir, ndef);
993                 else
994                         // Don't actually make a door
995                         roomplace -= doordir;
996                 
997         }
998 }
999
1000 static void make_nc(VoxelManipulator &vmanip, PseudoRandom &random,
1001                 INodeDefManager *ndef)
1002 {
1003         v3s16 dir;
1004         u8 facedir_i = 0;
1005         s32 r = random.range(0, 3);
1006         if(r == 0){
1007                 dir = v3s16( 1, 0, 0);
1008                 facedir_i = 3;
1009         }
1010         if(r == 1){
1011                 dir = v3s16(-1, 0, 0);
1012                 facedir_i = 1;
1013         }
1014         if(r == 2){
1015                 dir = v3s16( 0, 0, 1);
1016                 facedir_i = 2;
1017         }
1018         if(r == 3){
1019                 dir = v3s16( 0, 0,-1);
1020                 facedir_i = 0;
1021         }
1022         v3s16 p = vmanip.m_area.MinEdge + v3s16(
1023                         16+random.range(0,15),
1024                         16+random.range(0,15),
1025                         16+random.range(0,15));
1026         vmanip.m_data[vmanip.m_area.index(p)] = MapNode(LEGN(ndef, "CONTENT_NC"), facedir_i);
1027         u32 length = random.range(3,15);
1028         for(u32 j=0; j<length; j++)
1029         {
1030                 p -= dir;
1031                 vmanip.m_data[vmanip.m_area.index(p)] = MapNode(LEGN(ndef, "CONTENT_NC_RB"));
1032         }
1033 }
1034
1035 /*
1036         Noise functions. Make sure seed is mangled differently in each one.
1037 */
1038
1039 /*
1040         Scaling the output of the noise function affects the overdrive of the
1041         contour function, which affects the shape of the output considerably.
1042 */
1043 #define CAVE_NOISE_SCALE 12.0
1044 //#define CAVE_NOISE_SCALE 10.0
1045 //#define CAVE_NOISE_SCALE 7.5
1046 //#define CAVE_NOISE_SCALE 5.0
1047 //#define CAVE_NOISE_SCALE 1.0
1048
1049 //#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
1050 #define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE)
1051
1052 NoiseParams get_cave_noise1_params(u64 seed)
1053 {
1054         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
1055                         200, CAVE_NOISE_SCALE);*/
1056         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
1057                         100, CAVE_NOISE_SCALE);*/
1058         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6,
1059                         100, CAVE_NOISE_SCALE);*/
1060         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3,
1061                         100, CAVE_NOISE_SCALE);*/
1062         return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5,
1063                         50, CAVE_NOISE_SCALE);
1064         //return NoiseParams(NOISE_CONSTANT_ONE);
1065 }
1066
1067 NoiseParams get_cave_noise2_params(u64 seed)
1068 {
1069         /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
1070                         200, CAVE_NOISE_SCALE);*/
1071         /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
1072                         100, CAVE_NOISE_SCALE);*/
1073         /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3,
1074                         100, CAVE_NOISE_SCALE);*/
1075         return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5,
1076                         50, CAVE_NOISE_SCALE);
1077         //return NoiseParams(NOISE_CONSTANT_ONE);
1078 }
1079
1080 NoiseParams get_ground_noise1_params(u64 seed)
1081 {
1082         return NoiseParams(NOISE_PERLIN, seed+983240, 4,
1083                         0.55, 80.0, 40.0);
1084 }
1085
1086 NoiseParams get_ground_crumbleness_params(u64 seed)
1087 {
1088         return NoiseParams(NOISE_PERLIN, seed+34413, 3,
1089                         1.3, 20.0, 1.0);
1090 }
1091
1092 NoiseParams get_ground_wetness_params(u64 seed)
1093 {
1094         return NoiseParams(NOISE_PERLIN, seed+32474, 4,
1095                         1.1, 40.0, 1.0);
1096 }
1097
1098 bool is_cave(u64 seed, v3s16 p)
1099 {
1100         double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
1101         double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
1102         return d1*d2 > CAVE_NOISE_THRESHOLD;
1103 }
1104
1105 /*
1106         Ground density noise shall be interpreted by using this.
1107
1108         TODO: No perlin noises here, they should be outsourced
1109               and buffered
1110                   NOTE: The speed of these actually isn't terrible
1111 */
1112 bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
1113 {
1114         //return ((double)p.Y < ground_noise1_val);
1115
1116         double f = 0.55 + noise2d_perlin(
1117                         0.5+(float)p.X/250, 0.5+(float)p.Z/250,
1118                         seed+920381, 3, 0.45);
1119         if(f < 0.01)
1120                 f = 0.01;
1121         else if(f >= 1.0)
1122                 f *= 1.6;
1123         double h = WATER_LEVEL + 10 * noise2d_perlin(
1124                         0.5+(float)p.X/250, 0.5+(float)p.Z/250,
1125                         seed+84174, 4, 0.5);
1126         /*double f = 1;
1127         double h = 0;*/
1128         return ((double)p.Y - h < ground_noise1_val * f);
1129 }
1130
1131 /*
1132         Queries whether a position is ground or not.
1133 */
1134 bool is_ground(u64 seed, v3s16 p)
1135 {
1136         double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z);
1137         return val_is_ground(val1, p, seed);
1138 }
1139
1140 // Amount of trees per area in nodes
1141 double tree_amount_2d(u64 seed, v2s16 p)
1142 {
1143         /*double noise = noise2d_perlin(
1144                         0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1145                         seed+2, 5, 0.66);*/
1146         double noise = noise2d_perlin(
1147                         0.5+(float)p.X/125, 0.5+(float)p.Y/125,
1148                         seed+2, 4, 0.66);
1149         double zeroval = -0.39;
1150         if(noise < zeroval)
1151                 return 0;
1152         else
1153                 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1154 }
1155
1156 double surface_humidity_2d(u64 seed, v2s16 p)
1157 {
1158         double noise = noise2d_perlin(
1159                         0.5+(float)p.X/500, 0.5+(float)p.Y/500,
1160                         seed+72384, 4, 0.66);
1161         noise = (noise + 1.0)/2.0;
1162         if(noise < 0.0)
1163                 noise = 0.0;
1164         if(noise > 1.0)
1165                 noise = 1.0;
1166         return noise;
1167 }
1168
1169 #if 0
1170 double randomstone_amount_2d(u64 seed, v2s16 p)
1171 {
1172         double noise = noise2d_perlin(
1173                         0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1174                         seed+3829434, 5, 0.66);
1175         double zeroval = 0.1;
1176         if(noise < zeroval)
1177                 return 0;
1178         else
1179                 return 0.01 * (noise-zeroval) / (1.0-zeroval);
1180 }
1181 #endif
1182
1183 double largestone_amount_2d(u64 seed, v2s16 p)
1184 {
1185         double noise = noise2d_perlin(
1186                         0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1187                         seed+14143242, 5, 0.66);
1188         double zeroval = 0.3;
1189         if(noise < zeroval)
1190                 return 0;
1191         else
1192                 return 0.005 * (noise-zeroval) / (1.0-zeroval);
1193 }
1194
1195 /*
1196         Incrementally find ground level from 3d noise
1197 */
1198 s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
1199 {
1200         // Start a bit fuzzy to make averaging lower precision values
1201         // more useful
1202         s16 level = myrand_range(-precision/2, precision/2);
1203         s16 dec[] = {31000, 100, 20, 4, 1, 0};
1204         s16 i;
1205         for(i = 1; dec[i] != 0 && precision <= dec[i]; i++)
1206         {
1207                 // First find non-ground by going upwards
1208                 // Don't stop in caves.
1209                 {
1210                         s16 max = level+dec[i-1]*2;
1211                         v3s16 p(p2d.X, level, p2d.Y);
1212                         for(; p.Y < max; p.Y += dec[i])
1213                         {
1214                                 if(!is_ground(seed, p))
1215                                 {
1216                                         level = p.Y;
1217                                         break;
1218                                 }
1219                         }
1220                 }
1221                 // Then find ground by going downwards from there.
1222                 // Go in caves, too, when precision is 1.
1223                 {
1224                         s16 min = level-dec[i-1]*2;
1225                         v3s16 p(p2d.X, level, p2d.Y);
1226                         for(; p.Y>min; p.Y-=dec[i])
1227                         {
1228                                 bool ground = is_ground(seed, p);
1229                                 /*if(dec[i] == 1 && is_cave(seed, p))
1230                                         ground = false;*/
1231                                 if(ground)
1232                                 {
1233                                         level = p.Y;
1234                                         break;
1235                                 }
1236                         }
1237                 }
1238         }
1239         
1240         // This is more like the actual ground level
1241         level += dec[i-1]/2;
1242
1243         return level;
1244 }
1245
1246 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4);
1247
1248 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p)
1249 {
1250         v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
1251         v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
1252         double a = 0;
1253         a += find_ground_level_from_noise(seed,
1254                         v2s16(node_min.X, node_min.Y), p);
1255         a += find_ground_level_from_noise(seed,
1256                         v2s16(node_min.X, node_max.Y), p);
1257         a += find_ground_level_from_noise(seed,
1258                         v2s16(node_max.X, node_max.Y), p);
1259         a += find_ground_level_from_noise(seed,
1260                         v2s16(node_max.X, node_min.Y), p);
1261         a += find_ground_level_from_noise(seed,
1262                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p);
1263         a /= 5;
1264         return a;
1265 }
1266
1267 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
1268
1269 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
1270 {
1271         v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
1272         v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
1273         double a = -31000;
1274         // Corners
1275         a = MYMAX(a, find_ground_level_from_noise(seed,
1276                         v2s16(node_min.X, node_min.Y), p));
1277         a = MYMAX(a, find_ground_level_from_noise(seed,
1278                         v2s16(node_min.X, node_max.Y), p));
1279         a = MYMAX(a, find_ground_level_from_noise(seed,
1280                         v2s16(node_max.X, node_max.Y), p));
1281         a = MYMAX(a, find_ground_level_from_noise(seed,
1282                         v2s16(node_min.X, node_min.Y), p));
1283         // Center
1284         a = MYMAX(a, find_ground_level_from_noise(seed,
1285                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
1286         // Side middle points
1287         a = MYMAX(a, find_ground_level_from_noise(seed,
1288                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
1289         a = MYMAX(a, find_ground_level_from_noise(seed,
1290                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
1291         a = MYMAX(a, find_ground_level_from_noise(seed,
1292                         v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1293         a = MYMAX(a, find_ground_level_from_noise(seed,
1294                         v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1295         return a;
1296 }
1297
1298 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
1299
1300 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
1301 {
1302         v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
1303         v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
1304         double a = 31000;
1305         // Corners
1306         a = MYMIN(a, find_ground_level_from_noise(seed,
1307                         v2s16(node_min.X, node_min.Y), p));
1308         a = MYMIN(a, find_ground_level_from_noise(seed,
1309                         v2s16(node_min.X, node_max.Y), p));
1310         a = MYMIN(a, find_ground_level_from_noise(seed,
1311                         v2s16(node_max.X, node_max.Y), p));
1312         a = MYMIN(a, find_ground_level_from_noise(seed,
1313                         v2s16(node_min.X, node_min.Y), p));
1314         // Center
1315         a = MYMIN(a, find_ground_level_from_noise(seed,
1316                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
1317         // Side middle points
1318         a = MYMIN(a, find_ground_level_from_noise(seed,
1319                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
1320         a = MYMIN(a, find_ground_level_from_noise(seed,
1321                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
1322         a = MYMIN(a, find_ground_level_from_noise(seed,
1323                         v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1324         a = MYMIN(a, find_ground_level_from_noise(seed,
1325                         v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1326         return a;
1327 }
1328
1329 bool block_is_underground(u64 seed, v3s16 blockpos)
1330 {
1331         s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
1332                         seed, v2s16(blockpos.X, blockpos.Z));
1333         
1334         if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
1335                 return true;
1336         else
1337                 return false;
1338 }
1339
1340 #if 0
1341 #define AVERAGE_MUD_AMOUNT 4
1342
1343 double base_rock_level_2d(u64 seed, v2s16 p)
1344 {
1345         // The base ground level
1346         double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
1347                         + 20. * noise2d_perlin(
1348                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1349                         (seed>>32)+654879876, 6, 0.6);
1350
1351         /*// A bit hillier one
1352         double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
1353                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1354                         (seed>>27)+90340, 6, 0.69);
1355         if(base2 > base)
1356                 base = base2;*/
1357 #if 1
1358         // Higher ground level
1359         double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
1360                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1361                         seed+85039, 5, 0.69);
1362         //higher = 30; // For debugging
1363
1364         // Limit higher to at least base
1365         if(higher < base)
1366                 higher = base;
1367
1368         // Steepness factor of cliffs
1369         double b = 1.0 + 1.0 * noise2d_perlin(
1370                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1371                         seed-932, 7, 0.7);
1372         b = rangelim(b, 0.0, 1000.0);
1373         b = pow(b, 5);
1374         b *= 7;
1375         b = rangelim(b, 3.0, 1000.0);
1376         //dstream<<"b="<<b<<std::endl;
1377         //double b = 20;
1378
1379         // Offset to more low
1380         double a_off = -0.2;
1381         // High/low selector
1382         /*double a = 0.5 + b * (a_off + noise2d_perlin(
1383                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1384                         seed-359, 6, 0.7));*/
1385         double a = (double)0.5 + b * (a_off + noise2d_perlin(
1386                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1387                         seed-359, 5, 0.60));
1388         // Limit
1389         a = rangelim(a, 0.0, 1.0);
1390
1391         //dstream<<"a="<<a<<std::endl;
1392
1393         double h = base*(1.0-a) + higher*a;
1394 #else
1395         double h = base;
1396 #endif
1397         return h;
1398 }
1399
1400 double get_mud_add_amount(u64 seed, v2s16 p)
1401 {
1402         return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
1403                         0.5+(float)p.X/200, 0.5+(float)p.Y/200,
1404                         seed+91013, 3, 0.55));
1405 }
1406 #endif
1407
1408 bool get_have_sand(u64 seed, v2s16 p2d)
1409 {
1410         // Determine whether to have sand here
1411         double sandnoise = noise2d_perlin(
1412                         0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
1413                         seed+59420, 3, 0.50);
1414
1415         return (sandnoise > -0.15);
1416 }
1417
1418 /*
1419         Adds random objects to block, depending on the content of the block
1420 */
1421 void add_random_objects(MapBlock *block)
1422 {
1423 #if 0
1424         for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1425         for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1426         {
1427                 bool last_node_walkable = false;
1428                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1429                 {
1430                         v3s16 p(x0,y0,z0);
1431                         MapNode n = block->getNodeNoEx(p);
1432                         if(n.getContent() == CONTENT_IGNORE)
1433                                 continue;
1434                         if(data->nodedef->get(n)->liquid_type != LIQUID_NONE)
1435                                 continue;
1436                         if(data->nodedef->get(n)->walkable)
1437                         {
1438                                 last_node_walkable = true;
1439                                 continue;
1440                         }
1441                         if(last_node_walkable)
1442                         {
1443                                 // If block contains light information
1444                                 if(content_features(n).param_type == CPT_LIGHT)
1445                                 {
1446                                         if(n.getLight(LIGHTBANK_DAY) <= 3)
1447                                         {
1448                                                 if(myrand() % 300 == 0)
1449                                                 {
1450                                                         v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
1451                                                         pos_f.Y -= BS*0.4;
1452                                                         ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
1453                                                         std::string data = obj->getStaticData();
1454                                                         StaticObject s_obj(obj->getType(),
1455                                                                         obj->getBasePosition(), data);
1456                                                         // Add some
1457                                                         block->m_static_objects.insert(0, s_obj);
1458                                                         block->m_static_objects.insert(0, s_obj);
1459                                                         block->m_static_objects.insert(0, s_obj);
1460                                                         block->m_static_objects.insert(0, s_obj);
1461                                                         block->m_static_objects.insert(0, s_obj);
1462                                                         block->m_static_objects.insert(0, s_obj);
1463                                                         delete obj;
1464                                                 }
1465                                                 if(myrand() % 1000 == 0)
1466                                                 {
1467                                                         v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
1468                                                         pos_f.Y -= BS*0.4;
1469                                                         ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
1470                                                         std::string data = obj->getStaticData();
1471                                                         StaticObject s_obj(obj->getType(),
1472                                                                         obj->getBasePosition(), data);
1473                                                         // Add one
1474                                                         block->m_static_objects.insert(0, s_obj);
1475                                                         delete obj;
1476                                                 }
1477                                         }
1478                                 }
1479                         }
1480                         last_node_walkable = false;
1481                 }
1482         }
1483         block->raiseModified(MOD_STATE_WRITE_NEEDED, "mapgen::add_random_objects");
1484 #endif
1485 }
1486
1487 void make_block(BlockMakeData *data)
1488 {
1489         if(data->no_op)
1490         {
1491                 //dstream<<"makeBlock: no-op"<<std::endl;
1492                 return;
1493         }
1494
1495         assert(data->vmanip);
1496         assert(data->nodedef);
1497         assert(data->blockpos_requested.X >= data->blockpos_min.X &&
1498                         data->blockpos_requested.Y >= data->blockpos_min.Y &&
1499                         data->blockpos_requested.Z >= data->blockpos_min.Z);
1500         assert(data->blockpos_requested.X <= data->blockpos_max.X &&
1501                         data->blockpos_requested.Y <= data->blockpos_max.Y &&
1502                         data->blockpos_requested.Z <= data->blockpos_max.Z);
1503
1504         INodeDefManager *ndef = data->nodedef;
1505
1506         // Hack: use minimum block coordinates for old code that assumes
1507         // a single block
1508         v3s16 blockpos = data->blockpos_requested;
1509         
1510         /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
1511                         <<blockpos.Z<<")"<<std::endl;*/
1512
1513         v3s16 blockpos_min = data->blockpos_min;
1514         v3s16 blockpos_max = data->blockpos_max;
1515         v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1);
1516         v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1);
1517         
1518         ManualMapVoxelManipulator &vmanip = *(data->vmanip);
1519         // Area of center block
1520         v3s16 node_min = blockpos_min*MAP_BLOCKSIZE;
1521         v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
1522         // Full allocated area
1523         v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
1524         v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
1525
1526         v2s16 p2d_center(node_min.X+MAP_BLOCKSIZE/2, node_min.Z+MAP_BLOCKSIZE/2);
1527
1528         int rel_volume = (blockpos_max.X - blockpos_min.X + 1)
1529                         * (blockpos_max.Y - blockpos_min.Y + 1)
1530                         * (blockpos_max.Z - blockpos_max.Z + 1);
1531         
1532         // Area of the block we are generating
1533         double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume;
1534
1535         /*
1536                 Get average ground level from noise
1537                 TODO: These are currently crap because they assume we are
1538                       dealing with a single MapBlock only. Fix them.
1539         */
1540         
1541         s16 approx_groundlevel = (s16)get_sector_average_ground_level(
1542                         data->seed, v2s16(blockpos.X, blockpos.Z));
1543         //dstream<<"approx_groundlevel="<<approx_groundlevel<<std::endl;
1544         
1545         s16 approx_ground_depth = approx_groundlevel - (node_min.Y+MAP_BLOCKSIZE/2);
1546         
1547         s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
1548                         data->seed, v2s16(blockpos.X, blockpos.Z));
1549         // Minimum amount of ground above the top of the central block
1550         s16 minimum_ground_depth = minimum_groundlevel - node_max.Y;
1551
1552         s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level(
1553                         data->seed, v2s16(blockpos.X, blockpos.Z), 1);
1554         // Maximum amount of ground above the bottom of the central block
1555         s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
1556         
1557         // Horribly wrong heuristic, but better than nothing
1558         bool block_is_underground = (minimum_ground_depth > 
1559                         MAP_BLOCKSIZE * (data->blockpos_max.X
1560                                         - data->blockpos_min.X + 1) / 2);
1561
1562         /*
1563                 If block is deep underground, this is set to true and ground
1564                 density noise is not generated, for speed optimization.
1565         */
1566         bool all_is_ground_except_caves = (minimum_ground_depth > 40);
1567         
1568         /*
1569                 Create a block-specific seed
1570         */
1571         u32 blockseed = (u32)(data->seed%0x100000000ULL) + full_node_min.Z*38134234
1572                         + full_node_min.Y*42123 + full_node_min.X*23;
1573         
1574         /*
1575                 Make some 3D noise
1576         */
1577         
1578         //NoiseBuffer noisebuf1;
1579         //NoiseBuffer noisebuf2;
1580         NoiseBuffer noisebuf_cave;
1581         NoiseBuffer noisebuf_ground;
1582         NoiseBuffer noisebuf_ground_crumbleness;
1583         NoiseBuffer noisebuf_ground_wetness;
1584         {
1585                 v3f minpos_f(node_min.X, node_min.Y, node_min.Z);
1586                 v3f maxpos_f(node_max.X, node_max.Y, node_max.Z);
1587
1588                 //TimeTaker timer("noisebuf.create");
1589
1590                 /*
1591                         Cave noise
1592                 */
1593 #if 1
1594                 noisebuf_cave.create(get_cave_noise1_params(data->seed),
1595                                 minpos_f.X, minpos_f.Y, minpos_f.Z,
1596                                 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
1597                                 2, 2, 2);
1598                 noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
1599 #endif
1600
1601                 /*
1602                         Ground noise
1603                 */
1604                 
1605                 // Sample length
1606                 v3f sl = v3f(4.0, 4.0, 4.0);
1607                 
1608                 /*
1609                         Density noise
1610                 */
1611                 if(all_is_ground_except_caves == false)
1612                         //noisebuf_ground.create(data->seed+983240, 6, 0.60, false,
1613                         noisebuf_ground.create(get_ground_noise1_params(data->seed),
1614                                         minpos_f.X, minpos_f.Y, minpos_f.Z,
1615                                         maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
1616                                         sl.X, sl.Y, sl.Z);
1617                 
1618                 /*
1619                         Ground property noise
1620                 */
1621                 sl = v3f(2.5, 2.5, 2.5);
1622                 noisebuf_ground_crumbleness.create(
1623                                 get_ground_crumbleness_params(data->seed),
1624                                 minpos_f.X, minpos_f.Y, minpos_f.Z,
1625                                 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
1626                                 sl.X, sl.Y, sl.Z);
1627                 noisebuf_ground_wetness.create(
1628                                 get_ground_wetness_params(data->seed),
1629                                 minpos_f.X, minpos_f.Y, minpos_f.Z,
1630                                 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
1631                                 sl.X, sl.Y, sl.Z);
1632         }
1633
1634         /*
1635                 Cache some ground type values for speed
1636         */
1637
1638 // Creates variables c_name=id and n_name=node
1639 #define CONTENT_VARIABLE(ndef, name)\
1640         content_t c_##name = ndef->getId(#name);\
1641         MapNode n_##name(c_##name);
1642
1643         CONTENT_VARIABLE(ndef, stone);
1644         CONTENT_VARIABLE(ndef, air);
1645         CONTENT_VARIABLE(ndef, water_source);
1646         CONTENT_VARIABLE(ndef, dirt);
1647         CONTENT_VARIABLE(ndef, sand);
1648         CONTENT_VARIABLE(ndef, gravel);
1649         CONTENT_VARIABLE(ndef, clay);
1650         CONTENT_VARIABLE(ndef, lava_source);
1651         CONTENT_VARIABLE(ndef, cobble);
1652         CONTENT_VARIABLE(ndef, mossycobble);
1653         CONTENT_VARIABLE(ndef, dirt_with_grass);
1654         CONTENT_VARIABLE(ndef, junglegrass);
1655         CONTENT_VARIABLE(ndef, stone_with_coal);
1656         CONTENT_VARIABLE(ndef, stone_with_iron);
1657         CONTENT_VARIABLE(ndef, mese);
1658
1659         /*
1660                 Make base ground level
1661         */
1662
1663         for(s16 x=node_min.X; x<=node_max.X; x++)
1664         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1665         {
1666                 // Node position
1667                 v2s16 p2d(x,z);
1668                 {
1669                         // Use fast index incrementing
1670                         v3s16 em = vmanip.m_area.getExtent();
1671                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
1672                         for(s16 y=node_min.Y; y<=node_max.Y; y++)
1673                         {
1674                                 // Only modify places that have no content
1675                                 if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
1676                                 {
1677                                         // First priority: make air and water.
1678                                         // This avoids caves inside water.
1679                                         if(all_is_ground_except_caves == false
1680                                                         && val_is_ground(noisebuf_ground.get(x,y,z),
1681                                                         v3s16(x,y,z), data->seed) == false)
1682                                         {
1683                                                 if(y <= WATER_LEVEL)
1684                                                         vmanip.m_data[i] = n_water_source;
1685                                                 else
1686                                                         vmanip.m_data[i] = n_air;
1687                                         }
1688                                         else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
1689                                                 vmanip.m_data[i] = n_air;
1690                                         else
1691                                                 vmanip.m_data[i] = n_stone;
1692                                 }
1693                         
1694                                 data->vmanip->m_area.add_y(em, i, 1);
1695                         }
1696                 }
1697         }
1698
1699         /*
1700                 Add mud and sand and others underground (in place of stone)
1701         */
1702
1703         for(s16 x=node_min.X; x<=node_max.X; x++)
1704         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1705         {
1706                 // Node position
1707                 v2s16 p2d(x,z);
1708                 {
1709                         // Use fast index incrementing
1710                         v3s16 em = vmanip.m_area.getExtent();
1711                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
1712                         for(s16 y=node_max.Y; y>=node_min.Y; y--)
1713                         {
1714                                 if(vmanip.m_data[i].getContent() == c_stone)
1715                                 {
1716                                         if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
1717                                         {
1718                                                 if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
1719                                                         vmanip.m_data[i] = n_dirt;
1720                                                 else
1721                                                         vmanip.m_data[i] = n_sand;
1722                                         }
1723                                         else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
1724                                         {
1725                                                 if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
1726                                                         vmanip.m_data[i] = n_gravel;
1727                                         }
1728                                         else if(noisebuf_ground_crumbleness.get(x,y,z) <
1729                                                         -3.0 + MYMIN(0.1 * sqrt((float)MYMAX(0, -y)), 1.5))
1730                                         {
1731                                                 vmanip.m_data[i] = n_lava_source;
1732                                                 for(s16 x1=-1; x1<=1; x1++)
1733                                                 for(s16 y1=-1; y1<=1; y1++)
1734                                                 for(s16 z1=-1; z1<=1; z1++)
1735                                                         data->transforming_liquid.push_back(
1736                                                                         v3s16(p2d.X+x1, y+y1, p2d.Y+z1));
1737                                         }
1738                                 }
1739
1740                                 data->vmanip->m_area.add_y(em, i, -1);
1741                         }
1742                 }
1743         }
1744
1745         /*
1746                 Add dungeons
1747         */
1748         
1749         //if(node_min.Y < approx_groundlevel)
1750         //if(myrand() % 3 == 0)
1751         //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
1752         //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
1753         //float dungeon_rarity = g_settings.getFloat("dungeon_rarity");
1754         float dungeon_rarity = 0.02;
1755         if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0)
1756                         < dungeon_rarity
1757                         && node_min.Y < approx_groundlevel)
1758         {
1759                 // Dungeon generator doesn't modify places which have this set
1760                 data->vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
1761                                 | VMANIP_FLAG_DUNGEON_PRESERVE);
1762                 
1763                 // Set all air and water to be untouchable to make dungeons open
1764                 // to caves and open air
1765                 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
1766                 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
1767                 {
1768                         // Node position
1769                         v2s16 p2d(x,z);
1770                         {
1771                                 // Use fast index incrementing
1772                                 v3s16 em = vmanip.m_area.getExtent();
1773                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
1774                                 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
1775                                 {
1776                                         if(vmanip.m_data[i].getContent() == CONTENT_AIR)
1777                                                 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
1778                                         else if(vmanip.m_data[i].getContent() == c_water_source)
1779                                                 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
1780                                         data->vmanip->m_area.add_y(em, i, -1);
1781                                 }
1782                         }
1783                 }
1784                 
1785                 PseudoRandom random(blockseed+2);
1786
1787                 // Add it
1788                 make_dungeon1(vmanip, random, ndef);
1789                 
1790                 // Convert some cobble to mossy cobble
1791                 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
1792                 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
1793                 {
1794                         // Node position
1795                         v2s16 p2d(x,z);
1796                         {
1797                                 // Use fast index incrementing
1798                                 v3s16 em = vmanip.m_area.getExtent();
1799                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
1800                                 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
1801                                 {
1802                                         // (noisebuf not used because it doesn't contain the
1803                                         //  full area)
1804                                         double wetness = noise3d_param(
1805                                                         get_ground_wetness_params(data->seed), x,y,z);
1806                                         double d = noise3d_perlin((float)x/2.5,
1807                                                         (float)y/2.5,(float)z/2.5,
1808                                                         blockseed, 2, 1.4);
1809                                         if(vmanip.m_data[i].getContent() == c_cobble)
1810                                         {
1811                                                 if(d < wetness/3.0)
1812                                                 {
1813                                                         vmanip.m_data[i].setContent(c_mossycobble);
1814                                                 }
1815                                         }
1816                                         /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
1817                                         {
1818                                                 if(wetness > 1.2)
1819                                                         vmanip.m_data[i].setContent(c_dirt);
1820                                         }*/
1821                                         data->vmanip->m_area.add_y(em, i, -1);
1822                                 }
1823                         }
1824                 }
1825         }
1826
1827         /*
1828                 Add NC
1829         */
1830         {
1831                 PseudoRandom ncrandom(blockseed+9324342);
1832                 if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3)
1833                 {
1834                         make_nc(vmanip, ncrandom, ndef);
1835                 }
1836         }
1837         
1838         /*
1839                 Add top and bottom side of water to transforming_liquid queue
1840         */
1841
1842         for(s16 x=node_min.X; x<=node_max.X; x++)
1843         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1844         {
1845                 // Node position
1846                 v2s16 p2d(x,z);
1847                 {
1848                         bool water_found = false;
1849                         // Use fast index incrementing
1850                         v3s16 em = vmanip.m_area.getExtent();
1851                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
1852                         for(s16 y=node_max.Y; y>=node_min.Y; y--)
1853                         {
1854                                 if(water_found == false)
1855                                 {
1856                                         if(vmanip.m_data[i].getContent() == c_water_source)
1857                                         {
1858                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
1859                                                 data->transforming_liquid.push_back(p);
1860                                                 water_found = true;
1861                                         }
1862                                 }
1863                                 else
1864                                 {
1865                                         // This can be done because water_found can only
1866                                         // turn to true and end up here after going through
1867                                         // a single block.
1868                                         if(vmanip.m_data[i+1].getContent() != c_water_source)
1869                                         {
1870                                                 v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
1871                                                 data->transforming_liquid.push_back(p);
1872                                                 water_found = false;
1873                                         }
1874                                 }
1875
1876                                 data->vmanip->m_area.add_y(em, i, -1);
1877                         }
1878                 }
1879         }
1880
1881         /*
1882                 If close to ground level
1883         */
1884
1885         //if(abs(approx_ground_depth) < 30)
1886         if(minimum_ground_depth < 5 && maximum_ground_depth > -5)
1887         {
1888                 /*
1889                         Add grass and mud
1890                 */
1891
1892                 for(s16 x=node_min.X; x<=node_max.X; x++)
1893                 for(s16 z=node_min.Z; z<=node_max.Z; z++)
1894                 {
1895                         // Node position
1896                         v2s16 p2d(x,z);
1897                         {
1898                                 bool possibly_have_sand = get_have_sand(data->seed, p2d);
1899                                 bool have_sand = false;
1900                                 u32 current_depth = 0;
1901                                 bool air_detected = false;
1902                                 bool water_detected = false;
1903                                 bool have_clay = false;
1904
1905                                 // Use fast index incrementing
1906                                 s16 start_y = node_max.Y+2;
1907                                 v3s16 em = vmanip.m_area.getExtent();
1908                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
1909                                 for(s16 y=start_y; y>=node_min.Y-3; y--)
1910                                 {
1911                                         if(vmanip.m_data[i].getContent() == c_water_source)
1912                                                 water_detected = true;
1913                                         if(vmanip.m_data[i].getContent() == CONTENT_AIR)
1914                                                 air_detected = true;
1915
1916                                         if((vmanip.m_data[i].getContent() == c_stone
1917                                                         || vmanip.m_data[i].getContent() == c_dirt_with_grass
1918                                                         || vmanip.m_data[i].getContent() == c_dirt
1919                                                         || vmanip.m_data[i].getContent() == c_sand
1920                                                         || vmanip.m_data[i].getContent() == c_gravel
1921                                                         ) && (air_detected || water_detected))
1922                                         {
1923                                                 if(current_depth == 0 && y <= WATER_LEVEL+2
1924                                                                 && possibly_have_sand)
1925                                                         have_sand = true;
1926                                                 
1927                                                 if(current_depth < 4)
1928                                                 {
1929                                                         if(have_sand)
1930                                                         {
1931                                                                 // Determine whether to have clay in the sand here
1932                                                                 double claynoise = noise2d_perlin(
1933                                                                                 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
1934                                                                                 data->seed+4321, 6, 0.95) + 0.5;
1935                                 
1936                                                                 have_clay = (y <= WATER_LEVEL) && (y >= WATER_LEVEL-2) && (
1937                                                                         ((claynoise > 0) && (claynoise < 0.04) && (current_depth == 0)) ||
1938                                                                         ((claynoise > 0) && (claynoise < 0.12) && (current_depth == 1))
1939                                                                         );
1940                                                                 if (have_clay)
1941                                                                         vmanip.m_data[i] = MapNode(c_clay);
1942                                                                 else
1943                                                                         vmanip.m_data[i] = MapNode(c_sand);
1944                                                         }
1945                                                         #if 1
1946                                                         else if(current_depth==0 && !water_detected
1947                                                                         && y >= WATER_LEVEL && air_detected)
1948                                                                 vmanip.m_data[i] = MapNode(c_dirt_with_grass);
1949                                                         #endif
1950                                                         else
1951                                                                 vmanip.m_data[i] = MapNode(c_dirt);
1952                                                 }
1953                                                 else
1954                                                 {
1955                                                         if(vmanip.m_data[i].getContent() == c_dirt
1956                                                                 || vmanip.m_data[i].getContent() == c_dirt_with_grass)
1957                                                                 vmanip.m_data[i] = MapNode(c_stone);
1958                                                 }
1959
1960                                                 current_depth++;
1961
1962                                                 if(current_depth >= 8)
1963                                                         break;
1964                                         }
1965                                         else if(current_depth != 0)
1966                                                 break;
1967
1968                                         data->vmanip->m_area.add_y(em, i, -1);
1969                                 }
1970                         }
1971                 }
1972
1973                 /*
1974                         Calculate some stuff
1975                 */
1976                 
1977                 float surface_humidity = surface_humidity_2d(data->seed, p2d_center);
1978                 bool is_jungle = surface_humidity > 0.75;
1979                 // Amount of trees
1980                 u32 tree_count = gen_area_nodes * tree_amount_2d(data->seed, p2d_center);
1981                 if(is_jungle)
1982                         tree_count *= 5;
1983
1984                 /*
1985                         Add trees
1986                 */
1987                 PseudoRandom treerandom(blockseed);
1988                 // Put trees in random places on part of division
1989                 for(u32 i=0; i<tree_count; i++)
1990                 {
1991                         s16 x = treerandom.range(node_min.X, node_max.X);
1992                         s16 z = treerandom.range(node_min.Z, node_max.Z);
1993                         //s16 y = find_ground_level(data->vmanip, v2s16(x,z));
1994                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
1995                         // Don't make a tree under water level
1996                         if(y < WATER_LEVEL)
1997                                 continue;
1998                         // Make sure tree fits (only trees whose starting point is
1999                         // at this block are added)
2000                         if(y < node_min.Y || y > node_max.Y)
2001                                 continue;
2002                         /*
2003                                 Find exact ground level
2004                         */
2005                         v3s16 p(x,y+6,z);
2006                         bool found = false;
2007                         for(; p.Y >= y-6; p.Y--)
2008                         {
2009                                 u32 i = data->vmanip->m_area.index(p);
2010                                 MapNode *n = &data->vmanip->m_data[i];
2011                                 if(n->getContent() != CONTENT_AIR && n->getContent() != c_water_source && n->getContent() != CONTENT_IGNORE)
2012                                 {
2013                                         found = true;
2014                                         break;
2015                                 }
2016                         }
2017                         // If not found, handle next one
2018                         if(found == false)
2019                                 continue;
2020
2021                         {
2022                                 u32 i = data->vmanip->m_area.index(p);
2023                                 MapNode *n = &data->vmanip->m_data[i];
2024
2025                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass && n->getContent() != c_sand)
2026                                                 continue;
2027
2028                                 // Papyrus grows only on mud and in water
2029                                 if(n->getContent() == c_dirt && y <= WATER_LEVEL)
2030                                 {
2031                                         p.Y++;
2032                                         make_papyrus(vmanip, p, ndef);
2033                                 }
2034                                 // Trees grow only on mud and grass, on land
2035                                 else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2)
2036                                 {
2037                                         p.Y++;
2038                                         //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5)
2039                                         if(is_jungle == false)
2040                                         {
2041                                                 bool is_apple_tree;
2042                                                 if(myrand_range(0,4) != 0)
2043                                                         is_apple_tree = false;
2044                                                 else
2045                                                         is_apple_tree = noise2d_perlin(
2046                                                                         0.5+(float)p.X/100, 0.5+(float)p.Z/100,
2047                                                                         data->seed+342902, 3, 0.45) > 0.2;
2048                                                 make_tree(vmanip, p, is_apple_tree, ndef);
2049                                         }
2050                                         else
2051                                                 make_jungletree(vmanip, p, ndef);
2052                                 }
2053                                 // Cactii grow only on sand, on land
2054                                 else if(n->getContent() == c_sand && y > WATER_LEVEL + 2)
2055                                 {
2056                                         p.Y++;
2057                                         make_cactus(vmanip, p, ndef);
2058                                 }
2059                         }
2060                 }
2061
2062                 /*
2063                         Add jungle grass
2064                 */
2065                 if(is_jungle)
2066                 {
2067                         PseudoRandom grassrandom(blockseed);
2068                         for(u32 i=0; i<surface_humidity*5*tree_count; i++)
2069                         {
2070                                 s16 x = grassrandom.range(node_min.X, node_max.X);
2071                                 s16 z = grassrandom.range(node_min.Z, node_max.Z);
2072                                 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
2073                                 if(y < WATER_LEVEL)
2074                                         continue;
2075                                 if(y < node_min.Y || y > node_max.Y)
2076                                         continue;
2077                                 /*
2078                                         Find exact ground level
2079                                 */
2080                                 v3s16 p(x,y+6,z);
2081                                 bool found = false;
2082                                 for(; p.Y >= y-6; p.Y--)
2083                                 {
2084                                         u32 i = data->vmanip->m_area.index(p);
2085                                         MapNode *n = &data->vmanip->m_data[i];
2086                                         if(data->nodedef->get(*n).is_ground_content)
2087                                         {
2088                                                 found = true;
2089                                                 break;
2090                                         }
2091                                 }
2092                                 // If not found, handle next one
2093                                 if(found == false)
2094                                         continue;
2095                                 p.Y++;
2096                                 if(vmanip.m_area.contains(p) == false)
2097                                         continue;
2098                                 if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR)
2099                                         continue;
2100                                 /*p.Y--;
2101                                 if(vmanip.m_area.contains(p))
2102                                         vmanip.m_data[vmanip.m_area.index(p)] = c_dirt;
2103                                 p.Y++;*/
2104                                 if(vmanip.m_area.contains(p))
2105                                         vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass;
2106                         }
2107                 }
2108
2109 #if 0
2110                 /*
2111                         Add some kind of random stones
2112                 */
2113                 
2114                 u32 random_stone_count = gen_area_nodes *
2115                                 randomstone_amount_2d(data->seed, p2d_center);
2116                 // Put in random places on part of division
2117                 for(u32 i=0; i<random_stone_count; i++)
2118                 {
2119                         s16 x = myrand_range(node_min.X, node_max.X);
2120                         s16 z = myrand_range(node_min.Z, node_max.Z);
2121                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
2122                         // Don't add under water level
2123                         /*if(y < WATER_LEVEL)
2124                                 continue;*/
2125                         // Don't add if doesn't belong to this block
2126                         if(y < node_min.Y || y > node_max.Y)
2127                                 continue;
2128                         v3s16 p(x,y,z);
2129                         // Filter placement
2130                         /*{
2131                                 u32 i = data->vmanip->m_area.index(v3s16(p));
2132                                 MapNode *n = &data->vmanip->m_data[i];
2133                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
2134                                         continue;
2135                         }*/
2136                         // Will be placed one higher
2137                         p.Y++;
2138                         // Add it
2139                         make_randomstone(data->vmanip, p);
2140                 }
2141 #endif
2142
2143 #if 0
2144                 /*
2145                         Add larger stones
2146                 */
2147                 
2148                 u32 large_stone_count = gen_area_nodes *
2149                                 largestone_amount_2d(data->seed, p2d_center);
2150                 //u32 large_stone_count = 1;
2151                 // Put in random places on part of division
2152                 for(u32 i=0; i<large_stone_count; i++)
2153                 {
2154                         s16 x = myrand_range(node_min.X, node_max.X);
2155                         s16 z = myrand_range(node_min.Z, node_max.Z);
2156                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
2157                         // Don't add under water level
2158                         /*if(y < WATER_LEVEL)
2159                                 continue;*/
2160                         // Don't add if doesn't belong to this block
2161                         if(y < node_min.Y || y > node_max.Y)
2162                                 continue;
2163                         v3s16 p(x,y,z);
2164                         // Filter placement
2165                         /*{
2166                                 u32 i = data->vmanip->m_area.index(v3s16(p));
2167                                 MapNode *n = &data->vmanip->m_data[i];
2168                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
2169                                         continue;
2170                         }*/
2171                         // Will be placed one lower
2172                         p.Y--;
2173                         // Add it
2174                         make_largestone(data->vmanip, p);
2175                 }
2176 #endif
2177         }
2178
2179         /*
2180                 Add minerals
2181         */
2182
2183         {
2184                 PseudoRandom mineralrandom(blockseed);
2185
2186                 /*
2187                         Add meseblocks
2188                 */
2189                 for(s16 i=0; i<approx_ground_depth/4; i++)
2190                 {
2191                         if(mineralrandom.next()%50 == 0)
2192                         {
2193                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2194                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2195                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2196                                 for(u16 i=0; i<27; i++)
2197                                 {
2198                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2199                                         u32 vi = vmanip.m_area.index(p);
2200                                         if(vmanip.m_data[vi].getContent() == c_stone)
2201                                                 if(mineralrandom.next()%8 == 0)
2202                                                         vmanip.m_data[vi] = MapNode(c_mese);
2203                                 }
2204                                         
2205                         }
2206                 }
2207                 /*
2208                         Add others
2209                 */
2210                 {
2211                         u16 a = mineralrandom.range(0,15);
2212                         a = a*a*a;
2213                         u16 amount = 20 * a/1000;
2214                         for(s16 i=0; i<amount; i++)
2215                         {
2216                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2217                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2218                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2219
2220                                 u8 base_content = c_stone;
2221                                 MapNode new_content(CONTENT_IGNORE);
2222                                 u32 sparseness = 6;
2223
2224                                 if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
2225                                 {
2226                                         new_content = MapNode(c_stone_with_coal);
2227                                 }
2228                                 else
2229                                 {
2230                                         if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
2231                                                 new_content = MapNode(c_stone_with_iron);
2232                                         /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
2233                                                 vmanip.m_data[i] = MapNode(c_dirt);
2234                                         else
2235                                                 vmanip.m_data[i] = MapNode(c_sand);*/
2236                                 }
2237                                 /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
2238                                 {
2239                                 }*/
2240
2241                                 if(new_content.getContent() != CONTENT_IGNORE)
2242                                 {
2243                                         for(u16 i=0; i<27; i++)
2244                                         {
2245                                                 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2246                                                 u32 vi = vmanip.m_area.index(p);
2247                                                 if(vmanip.m_data[vi].getContent() == base_content)
2248                                                 {
2249                                                         if(mineralrandom.next()%sparseness == 0)
2250                                                                 vmanip.m_data[vi] = new_content;
2251                                                 }
2252                                         }
2253                                 }
2254                         }
2255                 }
2256                 /*
2257                         Add coal
2258                 */
2259                 //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
2260                 //for(s16 i=0; i<50; i++)
2261                 u16 coal_amount = 30;
2262                 u16 coal_rareness = 60 / coal_amount;
2263                 if(coal_rareness == 0)
2264                         coal_rareness = 1;
2265                 if(mineralrandom.next()%coal_rareness == 0)
2266                 {
2267                         u16 a = mineralrandom.next() % 16;
2268                         u16 amount = coal_amount * a*a*a / 1000;
2269                         for(s16 i=0; i<amount; i++)
2270                         {
2271                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2272                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2273                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2274                                 for(u16 i=0; i<27; i++)
2275                                 {
2276                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2277                                         u32 vi = vmanip.m_area.index(p);
2278                                         if(vmanip.m_data[vi].getContent() == c_stone)
2279                                                 if(mineralrandom.next()%8 == 0)
2280                                                         vmanip.m_data[vi] = MapNode(c_stone_with_coal);
2281                                 }
2282                         }
2283                 }
2284                 /*
2285                         Add iron
2286                 */
2287                 u16 iron_amount = 8;
2288                 u16 iron_rareness = 60 / iron_amount;
2289                 if(iron_rareness == 0)
2290                         iron_rareness = 1;
2291                 if(mineralrandom.next()%iron_rareness == 0)
2292                 {
2293                         u16 a = mineralrandom.next() % 16;
2294                         u16 amount = iron_amount * a*a*a / 1000;
2295                         for(s16 i=0; i<amount; i++)
2296                         {
2297                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2298                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2299                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2300                                 for(u16 i=0; i<27; i++)
2301                                 {
2302                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2303                                         u32 vi = vmanip.m_area.index(p);
2304                                         if(vmanip.m_data[vi].getContent() == c_stone)
2305                                                 if(mineralrandom.next()%8 == 0)
2306                                                         vmanip.m_data[vi] = MapNode(c_stone_with_iron);
2307                                 }
2308                         }
2309                 }
2310         }
2311
2312         /*
2313                 Calculate lighting
2314         */
2315         {
2316         ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update",
2317                         SPT_AVG);
2318         VoxelArea a(node_min, node_max);
2319         enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
2320         for(int i=0; i<2; i++)
2321         {
2322                 enum LightBank bank = banks[i];
2323
2324                 core::map<v3s16, bool> light_sources;
2325                 core::map<v3s16, u8> unlight_from;
2326
2327                 voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
2328                                 light_sources, unlight_from);
2329                 
2330                 bool inexistent_top_provides_sunlight = !block_is_underground;
2331                 voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
2332                                 vmanip, a, inexistent_top_provides_sunlight,
2333                                 light_sources, ndef);
2334                 // TODO: Do stuff according to bottom_sunlight_valid
2335
2336                 vmanip.unspreadLight(bank, unlight_from, light_sources, ndef);
2337
2338                 vmanip.spreadLight(bank, light_sources, ndef);
2339         }
2340         }
2341 }
2342
2343 BlockMakeData::BlockMakeData():
2344         no_op(false),
2345         vmanip(NULL),
2346         seed(0),
2347         nodedef(NULL)
2348 {}
2349
2350 BlockMakeData::~BlockMakeData()
2351 {
2352         delete vmanip;
2353 }
2354
2355 }; // namespace mapgen
2356
2357