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