0ce211f30d9a8a22c0812b4823825fec50ac2731
[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_min;
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         */
1538         
1539         s16 approx_groundlevel = (s16)get_sector_average_ground_level(
1540                         data->seed, v2s16(blockpos.X, blockpos.Z));
1541         //dstream<<"approx_groundlevel="<<approx_groundlevel<<std::endl;
1542         
1543         s16 approx_ground_depth = approx_groundlevel - (node_min.Y+MAP_BLOCKSIZE/2);
1544         
1545         s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
1546                         data->seed, v2s16(blockpos.X, blockpos.Z));
1547         // Minimum amount of ground above the top of the central block
1548         s16 minimum_ground_depth = minimum_groundlevel - node_max.Y;
1549
1550         s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level(
1551                         data->seed, v2s16(blockpos.X, blockpos.Z), 1);
1552         // Maximum amount of ground above the bottom of the central block
1553         s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
1554
1555         #if 1
1556         /*
1557                 Special case for high air or water: Just fill with air and water.
1558         */
1559         if(maximum_ground_depth < -20)
1560         {
1561                 for(s16 x=node_min.X; x<=node_max.X; x++)
1562                 for(s16 z=node_min.Z; z<=node_max.Z; z++)
1563                 {
1564                         // Node position
1565                         v2s16 p2d(x,z);
1566                         {
1567                                 // Use fast index incrementing
1568                                 v3s16 em = vmanip.m_area.getExtent();
1569                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
1570                                 for(s16 y=node_min.Y; y<=node_max.Y; y++)
1571                                 {
1572                                         // Only modify places that have no content
1573                                         if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
1574                                         {
1575                                                 if(y <= WATER_LEVEL)
1576                                                         vmanip.m_data[i] = MapNode(LEGN(ndef, "CONTENT_WATERSOURCE"));
1577                                                 else
1578                                                         vmanip.m_data[i] = MapNode(CONTENT_AIR);
1579                                         }
1580                                 
1581                                         data->vmanip->m_area.add_y(em, i, 1);
1582                                 }
1583                         }
1584                 }
1585                 
1586                 // We're done
1587                 return;
1588         }
1589         #endif
1590
1591         /*
1592                 If block is deep underground, this is set to true and ground
1593                 density noise is not generated, for speed optimization.
1594         */
1595         bool all_is_ground_except_caves = (minimum_ground_depth > 40);
1596         
1597         /*
1598                 Create a block-specific seed
1599         */
1600         u32 blockseed = (u32)(data->seed%0x100000000ULL) + full_node_min.Z*38134234
1601                         + full_node_min.Y*42123 + full_node_min.X*23;
1602         
1603         /*
1604                 Make some 3D noise
1605         */
1606         
1607         //NoiseBuffer noisebuf1;
1608         //NoiseBuffer noisebuf2;
1609         NoiseBuffer noisebuf_cave;
1610         NoiseBuffer noisebuf_ground;
1611         NoiseBuffer noisebuf_ground_crumbleness;
1612         NoiseBuffer noisebuf_ground_wetness;
1613         {
1614                 v3f minpos_f(node_min.X, node_min.Y, node_min.Z);
1615                 v3f maxpos_f(node_max.X, node_max.Y, node_max.Z);
1616
1617                 //TimeTaker timer("noisebuf.create");
1618
1619                 /*
1620                         Cave noise
1621                 */
1622 #if 1
1623                 noisebuf_cave.create(get_cave_noise1_params(data->seed),
1624                                 minpos_f.X, minpos_f.Y, minpos_f.Z,
1625                                 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
1626                                 2, 2, 2);
1627                 noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
1628 #endif
1629
1630                 /*
1631                         Ground noise
1632                 */
1633                 
1634                 // Sample length
1635                 v3f sl = v3f(4.0, 4.0, 4.0);
1636                 
1637                 /*
1638                         Density noise
1639                 */
1640                 if(all_is_ground_except_caves == false)
1641                         //noisebuf_ground.create(data->seed+983240, 6, 0.60, false,
1642                         noisebuf_ground.create(get_ground_noise1_params(data->seed),
1643                                         minpos_f.X, minpos_f.Y, minpos_f.Z,
1644                                         maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
1645                                         sl.X, sl.Y, sl.Z);
1646                 
1647                 /*
1648                         Ground property noise
1649                 */
1650                 sl = v3f(2.5, 2.5, 2.5);
1651                 noisebuf_ground_crumbleness.create(
1652                                 get_ground_crumbleness_params(data->seed),
1653                                 minpos_f.X, minpos_f.Y, minpos_f.Z,
1654                                 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
1655                                 sl.X, sl.Y, sl.Z);
1656                 noisebuf_ground_wetness.create(
1657                                 get_ground_wetness_params(data->seed),
1658                                 minpos_f.X, minpos_f.Y, minpos_f.Z,
1659                                 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
1660                                 sl.X, sl.Y, sl.Z);
1661         }
1662
1663         /*
1664                 Cache some ground type values for speed
1665         */
1666
1667 // Creates variables c_name=id and n_name=node
1668 #define CONTENT_VARIABLE(ndef, name)\
1669         content_t c_##name = ndef->getId(#name);\
1670         MapNode n_##name(c_##name);
1671
1672         CONTENT_VARIABLE(ndef, stone);
1673         CONTENT_VARIABLE(ndef, air);
1674         CONTENT_VARIABLE(ndef, water_source);
1675         CONTENT_VARIABLE(ndef, dirt);
1676         CONTENT_VARIABLE(ndef, sand);
1677         CONTENT_VARIABLE(ndef, gravel);
1678         CONTENT_VARIABLE(ndef, clay);
1679         CONTENT_VARIABLE(ndef, lava_source);
1680         CONTENT_VARIABLE(ndef, cobble);
1681         CONTENT_VARIABLE(ndef, mossycobble);
1682         CONTENT_VARIABLE(ndef, dirt_with_grass);
1683         CONTENT_VARIABLE(ndef, junglegrass);
1684         CONTENT_VARIABLE(ndef, stone_with_coal);
1685         CONTENT_VARIABLE(ndef, stone_with_iron);
1686         CONTENT_VARIABLE(ndef, mese);
1687
1688         /*
1689                 Make base ground level
1690         */
1691
1692         for(s16 x=node_min.X; x<=node_max.X; x++)
1693         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1694         {
1695                 // Node position
1696                 v2s16 p2d(x,z);
1697                 {
1698                         // Use fast index incrementing
1699                         v3s16 em = vmanip.m_area.getExtent();
1700                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
1701                         for(s16 y=node_min.Y; y<=node_max.Y; y++)
1702                         {
1703                                 // Only modify places that have no content
1704                                 if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
1705                                 {
1706                                         // First priority: make air and water.
1707                                         // This avoids caves inside water.
1708                                         if(all_is_ground_except_caves == false
1709                                                         && val_is_ground(noisebuf_ground.get(x,y,z),
1710                                                         v3s16(x,y,z), data->seed) == false)
1711                                         {
1712                                                 if(y <= WATER_LEVEL)
1713                                                         vmanip.m_data[i] = n_water_source;
1714                                                 else
1715                                                         vmanip.m_data[i] = n_air;
1716                                         }
1717                                         else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
1718                                                 vmanip.m_data[i] = n_air;
1719                                         else
1720                                                 vmanip.m_data[i] = n_stone;
1721                                 }
1722                         
1723                                 data->vmanip->m_area.add_y(em, i, 1);
1724                         }
1725                 }
1726         }
1727
1728         /*
1729                 Add mud and sand and others underground (in place of stone)
1730         */
1731
1732         for(s16 x=node_min.X; x<=node_max.X; x++)
1733         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1734         {
1735                 // Node position
1736                 v2s16 p2d(x,z);
1737                 {
1738                         // Use fast index incrementing
1739                         v3s16 em = vmanip.m_area.getExtent();
1740                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
1741                         for(s16 y=node_max.Y; y>=node_min.Y; y--)
1742                         {
1743                                 if(vmanip.m_data[i].getContent() == c_stone)
1744                                 {
1745                                         if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
1746                                         {
1747                                                 if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
1748                                                         vmanip.m_data[i] = n_dirt;
1749                                                 else
1750                                                         vmanip.m_data[i] = n_sand;
1751                                         }
1752                                         else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
1753                                         {
1754                                                 if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
1755                                                         vmanip.m_data[i] = n_gravel;
1756                                         }
1757                                         else if(noisebuf_ground_crumbleness.get(x,y,z) <
1758                                                         -3.0 + MYMIN(0.1 * sqrt((float)MYMAX(0, -y)), 1.5))
1759                                         {
1760                                                 vmanip.m_data[i] = n_lava_source;
1761                                                 for(s16 x1=-1; x1<=1; x1++)
1762                                                 for(s16 y1=-1; y1<=1; y1++)
1763                                                 for(s16 z1=-1; z1<=1; z1++)
1764                                                         data->transforming_liquid.push_back(
1765                                                                         v3s16(p2d.X+x1, y+y1, p2d.Y+z1));
1766                                         }
1767                                 }
1768
1769                                 data->vmanip->m_area.add_y(em, i, -1);
1770                         }
1771                 }
1772         }
1773
1774         /*
1775                 Add dungeons
1776         */
1777         
1778         //if(node_min.Y < approx_groundlevel)
1779         //if(myrand() % 3 == 0)
1780         //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
1781         //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
1782         //float dungeon_rarity = g_settings.getFloat("dungeon_rarity");
1783         float dungeon_rarity = 0.02;
1784         if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0)
1785                         < dungeon_rarity
1786                         && node_min.Y < approx_groundlevel)
1787         {
1788                 // Dungeon generator doesn't modify places which have this set
1789                 data->vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
1790                                 | VMANIP_FLAG_DUNGEON_PRESERVE);
1791                 
1792                 // Set all air and water to be untouchable to make dungeons open
1793                 // to caves and open air
1794                 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
1795                 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
1796                 {
1797                         // Node position
1798                         v2s16 p2d(x,z);
1799                         {
1800                                 // Use fast index incrementing
1801                                 v3s16 em = vmanip.m_area.getExtent();
1802                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
1803                                 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
1804                                 {
1805                                         if(vmanip.m_data[i].getContent() == CONTENT_AIR)
1806                                                 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
1807                                         else if(vmanip.m_data[i].getContent() == c_water_source)
1808                                                 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
1809                                         data->vmanip->m_area.add_y(em, i, -1);
1810                                 }
1811                         }
1812                 }
1813                 
1814                 PseudoRandom random(blockseed+2);
1815
1816                 // Add it
1817                 make_dungeon1(vmanip, random, ndef);
1818                 
1819                 // Convert some cobble to mossy cobble
1820                 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
1821                 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
1822                 {
1823                         // Node position
1824                         v2s16 p2d(x,z);
1825                         {
1826                                 // Use fast index incrementing
1827                                 v3s16 em = vmanip.m_area.getExtent();
1828                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
1829                                 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
1830                                 {
1831                                         // (noisebuf not used because it doesn't contain the
1832                                         //  full area)
1833                                         double wetness = noise3d_param(
1834                                                         get_ground_wetness_params(data->seed), x,y,z);
1835                                         double d = noise3d_perlin((float)x/2.5,
1836                                                         (float)y/2.5,(float)z/2.5,
1837                                                         blockseed, 2, 1.4);
1838                                         if(vmanip.m_data[i].getContent() == c_cobble)
1839                                         {
1840                                                 if(d < wetness/3.0)
1841                                                 {
1842                                                         vmanip.m_data[i].setContent(c_mossycobble);
1843                                                 }
1844                                         }
1845                                         /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
1846                                         {
1847                                                 if(wetness > 1.2)
1848                                                         vmanip.m_data[i].setContent(c_dirt);
1849                                         }*/
1850                                         data->vmanip->m_area.add_y(em, i, -1);
1851                                 }
1852                         }
1853                 }
1854         }
1855
1856         /*
1857                 Add NC
1858         */
1859         {
1860                 PseudoRandom ncrandom(blockseed+9324342);
1861                 if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3)
1862                 {
1863                         make_nc(vmanip, ncrandom, ndef);
1864                 }
1865         }
1866         
1867         /*
1868                 Add top and bottom side of water to transforming_liquid queue
1869         */
1870
1871         for(s16 x=node_min.X; x<=node_max.X; x++)
1872         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1873         {
1874                 // Node position
1875                 v2s16 p2d(x,z);
1876                 {
1877                         bool water_found = false;
1878                         // Use fast index incrementing
1879                         v3s16 em = vmanip.m_area.getExtent();
1880                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
1881                         for(s16 y=node_max.Y; y>=node_min.Y; y--)
1882                         {
1883                                 if(water_found == false)
1884                                 {
1885                                         if(vmanip.m_data[i].getContent() == c_water_source)
1886                                         {
1887                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
1888                                                 data->transforming_liquid.push_back(p);
1889                                                 water_found = true;
1890                                         }
1891                                 }
1892                                 else
1893                                 {
1894                                         // This can be done because water_found can only
1895                                         // turn to true and end up here after going through
1896                                         // a single block.
1897                                         if(vmanip.m_data[i+1].getContent() != c_water_source)
1898                                         {
1899                                                 v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
1900                                                 data->transforming_liquid.push_back(p);
1901                                                 water_found = false;
1902                                         }
1903                                 }
1904
1905                                 data->vmanip->m_area.add_y(em, i, -1);
1906                         }
1907                 }
1908         }
1909
1910         /*
1911                 If close to ground level
1912         */
1913
1914         //if(abs(approx_ground_depth) < 30)
1915         if(minimum_ground_depth < 5 && maximum_ground_depth > -5)
1916         {
1917                 /*
1918                         Add grass and mud
1919                 */
1920
1921                 for(s16 x=node_min.X; x<=node_max.X; x++)
1922                 for(s16 z=node_min.Z; z<=node_max.Z; z++)
1923                 {
1924                         // Node position
1925                         v2s16 p2d(x,z);
1926                         {
1927                                 bool possibly_have_sand = get_have_sand(data->seed, p2d);
1928                                 bool have_sand = false;
1929                                 u32 current_depth = 0;
1930                                 bool air_detected = false;
1931                                 bool water_detected = false;
1932                                 bool have_clay = false;
1933
1934                                 // Use fast index incrementing
1935                                 s16 start_y = node_max.Y+2;
1936                                 v3s16 em = vmanip.m_area.getExtent();
1937                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
1938                                 for(s16 y=start_y; y>=node_min.Y-3; y--)
1939                                 {
1940                                         if(vmanip.m_data[i].getContent() == c_water_source)
1941                                                 water_detected = true;
1942                                         if(vmanip.m_data[i].getContent() == CONTENT_AIR)
1943                                                 air_detected = true;
1944
1945                                         if((vmanip.m_data[i].getContent() == c_stone
1946                                                         || vmanip.m_data[i].getContent() == c_dirt_with_grass
1947                                                         || vmanip.m_data[i].getContent() == c_dirt
1948                                                         || vmanip.m_data[i].getContent() == c_sand
1949                                                         || vmanip.m_data[i].getContent() == c_gravel
1950                                                         ) && (air_detected || water_detected))
1951                                         {
1952                                                 if(current_depth == 0 && y <= WATER_LEVEL+2
1953                                                                 && possibly_have_sand)
1954                                                         have_sand = true;
1955                                                 
1956                                                 if(current_depth < 4)
1957                                                 {
1958                                                         if(have_sand)
1959                                                         {
1960                                                                 // Determine whether to have clay in the sand here
1961                                                                 double claynoise = noise2d_perlin(
1962                                                                                 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
1963                                                                                 data->seed+4321, 6, 0.95) + 0.5;
1964                                 
1965                                                                 have_clay = (y <= WATER_LEVEL) && (y >= WATER_LEVEL-2) && (
1966                                                                         ((claynoise > 0) && (claynoise < 0.04) && (current_depth == 0)) ||
1967                                                                         ((claynoise > 0) && (claynoise < 0.12) && (current_depth == 1))
1968                                                                         );
1969                                                                 if (have_clay)
1970                                                                         vmanip.m_data[i] = MapNode(c_clay);
1971                                                                 else
1972                                                                         vmanip.m_data[i] = MapNode(c_sand);
1973                                                         }
1974                                                         #if 1
1975                                                         else if(current_depth==0 && !water_detected
1976                                                                         && y >= WATER_LEVEL && air_detected)
1977                                                                 vmanip.m_data[i] = MapNode(c_dirt_with_grass);
1978                                                         #endif
1979                                                         else
1980                                                                 vmanip.m_data[i] = MapNode(c_dirt);
1981                                                 }
1982                                                 else
1983                                                 {
1984                                                         if(vmanip.m_data[i].getContent() == c_dirt
1985                                                                 || vmanip.m_data[i].getContent() == c_dirt_with_grass)
1986                                                                 vmanip.m_data[i] = MapNode(c_stone);
1987                                                 }
1988
1989                                                 current_depth++;
1990
1991                                                 if(current_depth >= 8)
1992                                                         break;
1993                                         }
1994                                         else if(current_depth != 0)
1995                                                 break;
1996
1997                                         data->vmanip->m_area.add_y(em, i, -1);
1998                                 }
1999                         }
2000                 }
2001
2002                 /*
2003                         Calculate some stuff
2004                 */
2005                 
2006                 float surface_humidity = surface_humidity_2d(data->seed, p2d_center);
2007                 bool is_jungle = surface_humidity > 0.75;
2008                 // Amount of trees
2009                 u32 tree_count = gen_area_nodes * tree_amount_2d(data->seed, p2d_center);
2010                 if(is_jungle)
2011                         tree_count *= 5;
2012
2013                 /*
2014                         Add trees
2015                 */
2016                 PseudoRandom treerandom(blockseed);
2017                 // Put trees in random places on part of division
2018                 for(u32 i=0; i<tree_count; i++)
2019                 {
2020                         s16 x = treerandom.range(node_min.X, node_max.X);
2021                         s16 z = treerandom.range(node_min.Z, node_max.Z);
2022                         //s16 y = find_ground_level(data->vmanip, v2s16(x,z));
2023                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
2024                         // Don't make a tree under water level
2025                         if(y < WATER_LEVEL)
2026                                 continue;
2027                         // Make sure tree fits (only trees whose starting point is
2028                         // at this block are added)
2029                         if(y < node_min.Y || y > node_max.Y)
2030                                 continue;
2031                         /*
2032                                 Find exact ground level
2033                         */
2034                         v3s16 p(x,y+6,z);
2035                         bool found = false;
2036                         for(; p.Y >= y-6; p.Y--)
2037                         {
2038                                 u32 i = data->vmanip->m_area.index(p);
2039                                 MapNode *n = &data->vmanip->m_data[i];
2040                                 if(n->getContent() != CONTENT_AIR && n->getContent() != c_water_source && n->getContent() != CONTENT_IGNORE)
2041                                 {
2042                                         found = true;
2043                                         break;
2044                                 }
2045                         }
2046                         // If not found, handle next one
2047                         if(found == false)
2048                                 continue;
2049
2050                         {
2051                                 u32 i = data->vmanip->m_area.index(p);
2052                                 MapNode *n = &data->vmanip->m_data[i];
2053
2054                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass && n->getContent() != c_sand)
2055                                                 continue;
2056
2057                                 // Papyrus grows only on mud and in water
2058                                 if(n->getContent() == c_dirt && y <= WATER_LEVEL)
2059                                 {
2060                                         p.Y++;
2061                                         make_papyrus(vmanip, p, ndef);
2062                                 }
2063                                 // Trees grow only on mud and grass, on land
2064                                 else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2)
2065                                 {
2066                                         p.Y++;
2067                                         //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5)
2068                                         if(is_jungle == false)
2069                                         {
2070                                                 bool is_apple_tree;
2071                                                 if(myrand_range(0,4) != 0)
2072                                                         is_apple_tree = false;
2073                                                 else
2074                                                         is_apple_tree = noise2d_perlin(
2075                                                                         0.5+(float)p.X/100, 0.5+(float)p.Z/100,
2076                                                                         data->seed+342902, 3, 0.45) > 0.2;
2077                                                 make_tree(vmanip, p, is_apple_tree, ndef);
2078                                         }
2079                                         else
2080                                                 make_jungletree(vmanip, p, ndef);
2081                                 }
2082                                 // Cactii grow only on sand, on land
2083                                 else if(n->getContent() == c_sand && y > WATER_LEVEL + 2)
2084                                 {
2085                                         p.Y++;
2086                                         make_cactus(vmanip, p, ndef);
2087                                 }
2088                         }
2089                 }
2090
2091                 /*
2092                         Add jungle grass
2093                 */
2094                 if(is_jungle)
2095                 {
2096                         PseudoRandom grassrandom(blockseed);
2097                         for(u32 i=0; i<surface_humidity*5*tree_count; i++)
2098                         {
2099                                 s16 x = grassrandom.range(node_min.X, node_max.X);
2100                                 s16 z = grassrandom.range(node_min.Z, node_max.Z);
2101                                 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
2102                                 if(y < WATER_LEVEL)
2103                                         continue;
2104                                 if(y < node_min.Y || y > node_max.Y)
2105                                         continue;
2106                                 /*
2107                                         Find exact ground level
2108                                 */
2109                                 v3s16 p(x,y+6,z);
2110                                 bool found = false;
2111                                 for(; p.Y >= y-6; p.Y--)
2112                                 {
2113                                         u32 i = data->vmanip->m_area.index(p);
2114                                         MapNode *n = &data->vmanip->m_data[i];
2115                                         if(data->nodedef->get(*n).is_ground_content)
2116                                         {
2117                                                 found = true;
2118                                                 break;
2119                                         }
2120                                 }
2121                                 // If not found, handle next one
2122                                 if(found == false)
2123                                         continue;
2124                                 p.Y++;
2125                                 if(vmanip.m_area.contains(p) == false)
2126                                         continue;
2127                                 if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR)
2128                                         continue;
2129                                 /*p.Y--;
2130                                 if(vmanip.m_area.contains(p))
2131                                         vmanip.m_data[vmanip.m_area.index(p)] = c_dirt;
2132                                 p.Y++;*/
2133                                 if(vmanip.m_area.contains(p))
2134                                         vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass;
2135                         }
2136                 }
2137
2138 #if 0
2139                 /*
2140                         Add some kind of random stones
2141                 */
2142                 
2143                 u32 random_stone_count = gen_area_nodes *
2144                                 randomstone_amount_2d(data->seed, p2d_center);
2145                 // Put in random places on part of division
2146                 for(u32 i=0; i<random_stone_count; i++)
2147                 {
2148                         s16 x = myrand_range(node_min.X, node_max.X);
2149                         s16 z = myrand_range(node_min.Z, node_max.Z);
2150                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
2151                         // Don't add under water level
2152                         /*if(y < WATER_LEVEL)
2153                                 continue;*/
2154                         // Don't add if doesn't belong to this block
2155                         if(y < node_min.Y || y > node_max.Y)
2156                                 continue;
2157                         v3s16 p(x,y,z);
2158                         // Filter placement
2159                         /*{
2160                                 u32 i = data->vmanip->m_area.index(v3s16(p));
2161                                 MapNode *n = &data->vmanip->m_data[i];
2162                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
2163                                         continue;
2164                         }*/
2165                         // Will be placed one higher
2166                         p.Y++;
2167                         // Add it
2168                         make_randomstone(data->vmanip, p);
2169                 }
2170 #endif
2171
2172 #if 0
2173                 /*
2174                         Add larger stones
2175                 */
2176                 
2177                 u32 large_stone_count = gen_area_nodes *
2178                                 largestone_amount_2d(data->seed, p2d_center);
2179                 //u32 large_stone_count = 1;
2180                 // Put in random places on part of division
2181                 for(u32 i=0; i<large_stone_count; i++)
2182                 {
2183                         s16 x = myrand_range(node_min.X, node_max.X);
2184                         s16 z = myrand_range(node_min.Z, node_max.Z);
2185                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
2186                         // Don't add under water level
2187                         /*if(y < WATER_LEVEL)
2188                                 continue;*/
2189                         // Don't add if doesn't belong to this block
2190                         if(y < node_min.Y || y > node_max.Y)
2191                                 continue;
2192                         v3s16 p(x,y,z);
2193                         // Filter placement
2194                         /*{
2195                                 u32 i = data->vmanip->m_area.index(v3s16(p));
2196                                 MapNode *n = &data->vmanip->m_data[i];
2197                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
2198                                         continue;
2199                         }*/
2200                         // Will be placed one lower
2201                         p.Y--;
2202                         // Add it
2203                         make_largestone(data->vmanip, p);
2204                 }
2205 #endif
2206         }
2207
2208         /*
2209                 Add minerals
2210         */
2211
2212         {
2213                 PseudoRandom mineralrandom(blockseed);
2214
2215                 /*
2216                         Add meseblocks
2217                 */
2218                 for(s16 i=0; i<approx_ground_depth/4; i++)
2219                 {
2220                         if(mineralrandom.next()%50 == 0)
2221                         {
2222                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2223                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2224                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2225                                 for(u16 i=0; i<27; i++)
2226                                 {
2227                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2228                                         u32 vi = vmanip.m_area.index(p);
2229                                         if(vmanip.m_data[vi].getContent() == c_stone)
2230                                                 if(mineralrandom.next()%8 == 0)
2231                                                         vmanip.m_data[vi] = MapNode(c_mese);
2232                                 }
2233                                         
2234                         }
2235                 }
2236                 /*
2237                         Add others
2238                 */
2239                 {
2240                         u16 a = mineralrandom.range(0,15);
2241                         a = a*a*a;
2242                         u16 amount = 20 * a/1000;
2243                         for(s16 i=0; i<amount; i++)
2244                         {
2245                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2246                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2247                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2248
2249                                 u8 base_content = c_stone;
2250                                 MapNode new_content(CONTENT_IGNORE);
2251                                 u32 sparseness = 6;
2252
2253                                 if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
2254                                 {
2255                                         new_content = MapNode(c_stone_with_coal);
2256                                 }
2257                                 else
2258                                 {
2259                                         if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
2260                                                 new_content = MapNode(c_stone_with_iron);
2261                                         /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
2262                                                 vmanip.m_data[i] = MapNode(c_dirt);
2263                                         else
2264                                                 vmanip.m_data[i] = MapNode(c_sand);*/
2265                                 }
2266                                 /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
2267                                 {
2268                                 }*/
2269
2270                                 if(new_content.getContent() != CONTENT_IGNORE)
2271                                 {
2272                                         for(u16 i=0; i<27; i++)
2273                                         {
2274                                                 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2275                                                 u32 vi = vmanip.m_area.index(p);
2276                                                 if(vmanip.m_data[vi].getContent() == base_content)
2277                                                 {
2278                                                         if(mineralrandom.next()%sparseness == 0)
2279                                                                 vmanip.m_data[vi] = new_content;
2280                                                 }
2281                                         }
2282                                 }
2283                         }
2284                 }
2285                 /*
2286                         Add coal
2287                 */
2288                 //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
2289                 //for(s16 i=0; i<50; i++)
2290                 u16 coal_amount = 30;
2291                 u16 coal_rareness = 60 / coal_amount;
2292                 if(coal_rareness == 0)
2293                         coal_rareness = 1;
2294                 if(mineralrandom.next()%coal_rareness == 0)
2295                 {
2296                         u16 a = mineralrandom.next() % 16;
2297                         u16 amount = coal_amount * a*a*a / 1000;
2298                         for(s16 i=0; i<amount; i++)
2299                         {
2300                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2301                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2302                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2303                                 for(u16 i=0; i<27; i++)
2304                                 {
2305                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2306                                         u32 vi = vmanip.m_area.index(p);
2307                                         if(vmanip.m_data[vi].getContent() == c_stone)
2308                                                 if(mineralrandom.next()%8 == 0)
2309                                                         vmanip.m_data[vi] = MapNode(c_stone_with_coal);
2310                                 }
2311                         }
2312                 }
2313                 /*
2314                         Add iron
2315                 */
2316                 u16 iron_amount = 8;
2317                 u16 iron_rareness = 60 / iron_amount;
2318                 if(iron_rareness == 0)
2319                         iron_rareness = 1;
2320                 if(mineralrandom.next()%iron_rareness == 0)
2321                 {
2322                         u16 a = mineralrandom.next() % 16;
2323                         u16 amount = iron_amount * a*a*a / 1000;
2324                         for(s16 i=0; i<amount; i++)
2325                         {
2326                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2327                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2328                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2329                                 for(u16 i=0; i<27; i++)
2330                                 {
2331                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2332                                         u32 vi = vmanip.m_area.index(p);
2333                                         if(vmanip.m_data[vi].getContent() == c_stone)
2334                                                 if(mineralrandom.next()%8 == 0)
2335                                                         vmanip.m_data[vi] = MapNode(c_stone_with_iron);
2336                                 }
2337                         }
2338                 }
2339         }
2340
2341         /*
2342                 Calculate lighting
2343         */
2344         {
2345         ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update",
2346                         SPT_AVG);
2347         VoxelArea a(node_min, node_max);
2348         enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
2349         for(int i=0; i<2; i++)
2350         {
2351                 enum LightBank bank = banks[i];
2352
2353                 core::map<v3s16, bool> light_sources;
2354                 core::map<v3s16, u8> unlight_from;
2355
2356                 voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
2357                                 light_sources, unlight_from);
2358                 
2359                 // TODO: Get this from elsewhere
2360                 bool inexistent_top_provides_sunlight = true;
2361                 voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
2362                                 vmanip, a, inexistent_top_provides_sunlight,
2363                                 light_sources, ndef);
2364                 // TODO: Do stuff according to bottom_sunlight_valid
2365
2366                 vmanip.unspreadLight(bank, unlight_from, light_sources, ndef);
2367
2368                 vmanip.spreadLight(bank, light_sources, ndef);
2369         }
2370         }
2371 }
2372
2373 BlockMakeData::BlockMakeData():
2374         no_op(false),
2375         vmanip(NULL),
2376         seed(0),
2377         nodedef(NULL)
2378 {}
2379
2380 BlockMakeData::~BlockMakeData()
2381 {
2382         delete vmanip;
2383 }
2384
2385 }; // namespace mapgen
2386
2387