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