]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapgen.cpp
Add initial Lua biomedef support, fixed biome selection
[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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapgen.h"
21 #include "voxel.h"
22 #include "noise.h"
23 #include "biome.h"
24 #include "mapblock.h"
25 #include "mapnode.h"
26 #include "map.h"
27 //#include "serverobject.h"
28 #include "content_sao.h"
29 #include "nodedef.h"
30 #include "content_mapnode.h" // For content_mapnode_get_new_name
31 #include "voxelalgorithms.h"
32 #include "profiler.h"
33 #include "settings.h" // For g_settings
34 #include "main.h" // For g_profiler
35 #include "treegen.h"
36
37 NoiseParams nparams_mtdefault =
38         {10.0, 12.0, v3f(350., 350., 350.), 82341, 5, 0.6}; //terrain
39 NoiseParams nparams_def_bgroup =
40         {0.5, 1/(2*1.6), v3f(350., 350., 350.), 5923, 2, 0.60}; //0 to 1
41 NoiseParams nparams_def_heat =
42         {25.0, 50.0, v3f(500., 500., 500.), 35293, 1, 0.00}; //-25 to 75
43 NoiseParams nparams_def_humidity =
44         {50, 100/(2*1.6), v3f(750., 750., 750.), 12094, 2, 0.60}; //0 to 100
45
46
47 ///////////////////////////////////////////////////////////////////////////////
48
49 /*
50 Mapgen::Mapgen(BiomeDefManager *biomedef) {
51         Mapgen(0, 0, biomedef);
52 }*/
53
54
55 Mapgen::Mapgen(BiomeDefManager *biomedef, int mapgenid, u64 seed) {
56         initMapgen(biomedef, mapgenid, seed,
57                 &nparams_mtdefault, &nparams_def_bgroup,
58                 &nparams_def_heat,  &nparams_def_humidity);
59 }
60
61
62 Mapgen::Mapgen(BiomeDefManager *biomedef, int mapgenid, u64 seed,
63                            NoiseParams *np_terrain, NoiseParams *np_bgroup,
64                            NoiseParams *np_heat,    NoiseParams *np_humidity) {
65         initMapgen(biomedef, mapgenid, seed,
66                 np_terrain, np_bgroup, np_heat, np_humidity);
67 }
68
69 void Mapgen::initMapgen(BiomeDefManager *biomedef, int mapgenid, u64 seed,
70                            NoiseParams *np_terrain, NoiseParams *np_bgroup,
71                            NoiseParams *np_heat,    NoiseParams *np_humidity) {
72         this->generating  = false;
73         this->id       = mapgenid;
74         this->seed     = (int)seed;
75         this->biomedef = biomedef;
76         this->csize       = v3s16(5, 5, 5) * MAP_BLOCKSIZE; /////////////////get this from config!
77         this->water_level = g_settings->getS16("default_water_level"); ////fix this!
78
79         this->np_terrain  = np_terrain;
80         this->np_bgroup   = np_bgroup;
81         this->np_heat     = np_heat;
82         this->np_humidity = np_humidity;
83         noise_terrain  = new Noise(np_terrain,  seed, csize.X, csize.Y);
84         noise_bgroup   = new Noise(np_bgroup,   seed, csize.X, csize.Y);
85         noise_heat     = new Noise(np_heat,     seed, csize.X, csize.Y);
86         noise_humidity = new Noise(np_humidity, seed, csize.X, csize.Y);
87
88         this->ndef = biomedef->ndef;
89         n_air   = MapNode(ndef->getId("mapgen_air"));
90         n_water = MapNode(ndef->getId("mapgen_water_source"));
91         n_lava  = MapNode(ndef->getId("mapgen_lava_source"));
92 }
93
94
95 Mapgen::~Mapgen() {
96         delete noise_terrain;
97         delete noise_bgroup;
98         delete noise_heat;
99         delete noise_humidity;
100 }
101
102
103 void Mapgen::makeChunk(BlockMakeData *data) {
104         if (data->no_op)
105                 return;
106
107         //printf("generating...\n");//////////////
108
109         assert(data->vmanip);
110         assert(data->nodedef);
111         assert(data->blockpos_requested.X >= data->blockpos_min.X &&
112                    data->blockpos_requested.Y >= data->blockpos_min.Y &&
113                    data->blockpos_requested.Z >= data->blockpos_min.Z);
114         assert(data->blockpos_requested.X <= data->blockpos_max.X &&
115                    data->blockpos_requested.Y <= data->blockpos_max.Y &&
116                    data->blockpos_requested.Z <= data->blockpos_max.Z);
117
118         this->generating = true;
119
120         this->data    = data;
121         this->vmanip  = data->vmanip;
122         v3s16 em = vmanip->m_area.getExtent();
123         this->ystride = em.X;
124         this->zstride = em.Y * em.X;
125
126         node_min = (data->blockpos_min) * MAP_BLOCKSIZE;
127         node_max = (data->blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
128         v3s16 full_node_min = (data->blockpos_min - 1) * MAP_BLOCKSIZE;
129         v3s16 full_node_max = (data->blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1,1,1);
130
131         int y1 = node_min.Y;
132         int y2 = node_max.Y;
133         int x  = node_min.X;
134         int z  = node_min.Z;
135         //printf("full_node_min.X: %d  |  full_node_min.Z:  %d   |  MinEdge:  %d   |  MaxEdge:  %d\n", node_min.X, node_min.Z, vmanip->m_area.MinEdge.X, vmanip->m_area.MinEdge.Z);
136         TimeTaker timer("Generating terrain");
137         map_terrain  = noise_terrain->perlinMap2D(x, z);
138         map_bgroup   = noise_bgroup->perlinMap2D(x, z);
139         map_heat     = noise_heat->perlinMap2D(x, z);
140         map_humidity = noise_humidity->perlinMap2D(x, z);
141
142         noise_bgroup->transformNoiseMap();
143         noise_heat->transformNoiseMap();
144         noise_humidity->transformNoiseMap();
145
146         int i = 0;
147         for (z = node_min.Z; z <= node_max.Z; z++) {
148                 for (x = node_min.X; x <= node_max.X; x++) {
149                         Biome *biome = biomedef->getBiome(map_bgroup[i], map_heat[i], map_humidity[i]);
150                         biome->genColumn(this, x, z, y1, y2);
151                         i++;
152                 }
153         }
154         timer.stop();
155
156         //genCave();
157         //genDungeon();
158         //add blobs of dirt and gravel underground
159         //decorateChunk();
160         updateLiquid(full_node_min, full_node_max);
161         updateLighting(node_min, node_max);
162
163         this->generating = false;
164         //printf("generated block (%d, %d) to (%d, %d)\n", node_min.X, node_min.Y, node_max.X, node_max.Y);//////////
165 }
166
167
168 void Mapgen::updateLiquid(v3s16 node_min, v3s16 node_max) {
169         bool isliquid, wasliquid;
170         u32 i;
171
172         for (s16 z = node_min.Z; z <= node_max.Z; z++) {
173                 for (s16 x = node_min.X; x <= node_max.X; x++) {
174                         v2s16 p2d(x, z);
175                         wasliquid = true;
176                         v3s16 em  = vmanip->m_area.getExtent();
177                         i = vmanip->m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
178
179                         for (s16 y = node_max.Y; y >= node_min.Y; y--) {
180                                 isliquid = ndef->get(vmanip->m_data[i]).isLiquid();
181                                 //there was a change between liquid and nonliquid, add to queue
182                                 if (isliquid != wasliquid)
183                                         data->transforming_liquid.push_back(v3s16(p2d.X, y, p2d.Y));
184
185                                 wasliquid = isliquid;
186                                 vmanip->m_area.add_y(em, i, -1);
187                         }
188                 }
189         }
190 }
191
192
193 void Mapgen::updateLighting(v3s16 node_min, v3s16 node_max) {
194         enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
195
196         VoxelArea a(node_min - v3s16(1,0,1) * MAP_BLOCKSIZE,
197                                 node_max + v3s16(1,0,1) * MAP_BLOCKSIZE);
198         bool block_is_underground = (water_level > node_max.Y);
199         bool sunlight = !block_is_underground;
200
201         ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
202         for (int i = 0; i < 2; i++) {
203                 enum LightBank bank = banks[i];
204         core::map<v3s16, bool> light_sources;
205         core::map<v3s16, u8> unlight_from;
206
207                 voxalgo::clearLightAndCollectSources(*vmanip, a, bank, ndef,
208                                         light_sources, unlight_from);
209                 voxalgo::propagateSunlight(*vmanip, a, sunlight, light_sources, ndef);
210         printf("light_sources: %d\t\tunlight_from: %d\n", light_sources.size(), unlight_from.size());
211                 vmanip->unspreadLight(bank, unlight_from, light_sources, ndef);
212                 vmanip->spreadLight(bank, light_sources, ndef);
213         }
214 }
215
216
217 /*class EmergeManager {
218 public:
219         int seed;
220         int water_level;
221         BiomeDefManager *biomedef;
222
223         //mapgen objects here
224
225         void addBlockToQueue();
226
227
228         //mapgen helper methods
229         int getGroundLevelAtPoint(u64 mseed, v2s16 p);
230         bool isBlockUnderground(u64 mseed, v3s16 blockpos);
231         u32 getBlockSeed(u64 mseed, v3s16 p);
232 };*/
233
234 EmergeManager::EmergeManager(IGameDef *gamedef) {
235         this->seed = 0;
236         this->water_level = 0;
237         this->np_terrain  = &nparams_mtdefault;
238         this->np_bgroup   = &nparams_def_bgroup;
239         this->np_heat     = &nparams_def_heat;
240         this->np_humidity = &nparams_def_humidity;
241
242         this->biomedef = new BiomeDefManager(gamedef);
243 }
244
245
246 EmergeManager::~EmergeManager() {
247         delete biomedef;
248 }
249
250
251 void EmergeManager::addBlockToQueue() {
252
253 }
254
255
256 Biome *EmergeManager::getBiomeAtPoint(v3s16 p) {
257         float bgroup   = NoisePerlin2D(np_bgroup,   p.X, p.Y, seed);
258         float heat     = NoisePerlin2D(np_heat,     p.X, p.Y, seed);
259         float humidity = NoisePerlin2D(np_humidity, p.X, p.Y, seed);
260         return biomedef->getBiome(bgroup, heat, humidity);
261 }
262
263
264 //FIXME:  This assumes y == 0, that is, always in a non-hell/non-sky biome
265 int EmergeManager::getGroundLevelAtPoint(v2s16 p) {
266         float terrain = NoisePerlin2D(np_terrain, p.X, p.Y, seed);
267         Biome *biome = getBiomeAtPoint(v3s16(p.X, p.Y, 0));
268         return biome->getSurfaceHeight(terrain);
269 }
270
271
272 bool EmergeManager::isBlockUnderground(v3s16 blockpos) {
273         /*
274         v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2,
275                                         (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2);
276         int ground_level = getGroundLevelAtPoint(p);
277         return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level);
278         */
279
280         //yuck, but then again, should i bother being accurate?
281         //the height of the nodes in a single block is quite variable
282         return false; //blockpos.Y * (MAP_BLOCKSIZE + 1) <= water_level;
283 }
284
285
286 u32 EmergeManager::getBlockSeed(v3s16 p) {
287         return (u32)(seed & 0xFFFFFFFF) +
288                 p.Z * 38134234 +
289                 p.Y * 42123 +
290                 p.Y * 23;
291 }
292
293
294 /////////////////////////////////// legacy static functions for farmesh
295
296
297 s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) {
298         //just need to return something
299         s16 level = 5;
300         return level;
301 }
302
303
304 bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) {
305         double sandnoise = noise2d_perlin(
306                         0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
307                         seed+59420, 3, 0.50);
308
309         return (sandnoise > 0.15);
310 }
311
312
313 double Mapgen::tree_amount_2d(u64 seed, v2s16 p) {
314         double noise = noise2d_perlin(
315                         0.5+(float)p.X/125, 0.5+(float)p.Y/125,
316                         seed+2, 4, 0.66);
317         double zeroval = -0.39;
318         if(noise < zeroval)
319                 return 0;
320         else
321                 return 0.04 * (noise-zeroval) / (1.0-zeroval);
322 }
323
324
325 #if 0 /// BIG COMMENT
326 namespace mapgen
327 {
328
329 /*
330         Some helper functions for the map generator
331 */
332
333 #if 1
334 // Returns Y one under area minimum if not found
335 static s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d,
336                 INodeDefManager *ndef)
337 {
338         v3s16 em = vmanip.m_area.getExtent();
339         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
340         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
341         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
342         s16 y;
343         for(y=y_nodes_max; y>=y_nodes_min; y--)
344         {
345                 MapNode &n = vmanip.m_data[i];
346                 if(ndef->get(n).walkable)
347                         break;
348
349                 vmanip.m_area.add_y(em, i, -1);
350         }
351         if(y >= y_nodes_min)
352                 return y;
353         else
354                 return y_nodes_min - 1;
355 }
356
357 #if 0
358 // Returns Y one under area minimum if not found
359 static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d,
360                 INodeDefManager *ndef)
361 {
362         if(!vmanip.m_area.contains(v3s16(p2d.X, vmanip.m_area.MaxEdge.Y, p2d.Y)))
363                 return vmanip.m_area.MinEdge.Y-1;
364         v3s16 em = vmanip.m_area.getExtent();
365         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
366         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
367         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
368         s16 y;
369         content_t c_tree = ndef->getId("mapgen_tree");
370         content_t c_leaves = ndef->getId("mapgen_leaves");
371         for(y=y_nodes_max; y>=y_nodes_min; y--)
372         {
373                 MapNode &n = vmanip.m_data[i];
374                 if(ndef->get(n).walkable
375                                 && n.getContent() != c_tree
376                                 && n.getContent() != c_leaves)
377                         break;
378
379                 vmanip.m_area.add_y(em, i, -1);
380         }
381         if(y >= y_nodes_min)
382                 return y;
383         else
384                 return y_nodes_min - 1;
385 }
386 #endif
387
388 // Returns Y one under area minimum if not found
389 static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d,
390                 INodeDefManager *ndef)
391 {
392         v3s16 em = vmanip.m_area.getExtent();
393         s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
394         s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
395         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
396         s16 y;
397         content_t c_stone = ndef->getId("mapgen_stone");
398         content_t c_desert_stone = ndef->getId("mapgen_desert_stone");
399         for(y=y_nodes_max; y>=y_nodes_min; y--)
400         {
401                 MapNode &n = vmanip.m_data[i];
402                 content_t c = n.getContent();
403                 if(c != CONTENT_IGNORE && (
404                                 c == c_stone || c == c_desert_stone))
405                         break;
406
407                 vmanip.m_area.add_y(em, i, -1);
408         }
409         if(y >= y_nodes_min)
410                 return y;
411         else
412                 return y_nodes_min - 1;
413 }
414 #endif
415
416
417 #if 0
418
419 static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0,
420                 INodeDefManager *ndef)
421 {
422         MapNode papyrusnode(ndef->getId("mapgen_papyrus"));
423
424         s16 trunk_h = myrand_range(2, 3);
425         v3s16 p1 = p0;
426         for(s16 ii=0; ii<trunk_h; ii++)
427         {
428                 if(vmanip.m_area.contains(p1))
429                         vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode;
430                 p1.Y++;
431         }
432 }
433
434 static void make_cactus(VoxelManipulator &vmanip, v3s16 p0,
435                 INodeDefManager *ndef)
436 {
437         MapNode cactusnode(ndef->getId("mapgen_cactus"));
438
439         s16 trunk_h = 3;
440         v3s16 p1 = p0;
441         for(s16 ii=0; ii<trunk_h; ii++)
442         {
443                 if(vmanip.m_area.contains(p1))
444                         vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode;
445                 p1.Y++;
446         }
447 }
448 #endif
449
450 #if 0
451 /*
452         Dungeon making routines
453 */
454
455 #define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
456 #define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
457 #define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
458                 VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
459
460 static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace,
461                 INodeDefManager *ndef)
462 {
463         // Make +-X walls
464         for(s16 z=0; z<roomsize.Z; z++)
465         for(s16 y=0; y<roomsize.Y; y++)
466         {
467                 {
468                         v3s16 p = roomplace + v3s16(0,y,z);
469                         if(vmanip.m_area.contains(p) == false)
470                                 continue;
471                         u32 vi = vmanip.m_area.index(p);
472                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
473                                 continue;
474                         vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
475                 }
476                 {
477                         v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
478                         if(vmanip.m_area.contains(p) == false)
479                                 continue;
480                         u32 vi = vmanip.m_area.index(p);
481                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
482                                 continue;
483                         vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
484                 }
485         }
486
487         // Make +-Z walls
488         for(s16 x=0; x<roomsize.X; x++)
489         for(s16 y=0; y<roomsize.Y; y++)
490         {
491                 {
492                         v3s16 p = roomplace + v3s16(x,y,0);
493                         if(vmanip.m_area.contains(p) == false)
494                                 continue;
495                         u32 vi = vmanip.m_area.index(p);
496                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
497                                 continue;
498                         vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
499                 }
500                 {
501                         v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
502                         if(vmanip.m_area.contains(p) == false)
503                                 continue;
504                         u32 vi = vmanip.m_area.index(p);
505                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
506                                 continue;
507                         vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
508                 }
509         }
510
511         // Make +-Y walls (floor and ceiling)
512         for(s16 z=0; z<roomsize.Z; z++)
513         for(s16 x=0; x<roomsize.X; x++)
514         {
515                 {
516                         v3s16 p = roomplace + v3s16(x,0,z);
517                         if(vmanip.m_area.contains(p) == false)
518                                 continue;
519                         u32 vi = vmanip.m_area.index(p);
520                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
521                                 continue;
522                         vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
523                 }
524                 {
525                         v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
526                         if(vmanip.m_area.contains(p) == false)
527                                 continue;
528                         u32 vi = vmanip.m_area.index(p);
529                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
530                                 continue;
531                         vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble"));
532                 }
533         }
534
535         // Fill with air
536         for(s16 z=1; z<roomsize.Z-1; z++)
537         for(s16 y=1; y<roomsize.Y-1; y++)
538         for(s16 x=1; x<roomsize.X-1; x++)
539         {
540                 v3s16 p = roomplace + v3s16(x,y,z);
541                 if(vmanip.m_area.contains(p) == false)
542                         continue;
543                 u32 vi = vmanip.m_area.index(p);
544                 vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
545                 vmanip.m_data[vi] = MapNode(CONTENT_AIR);
546         }
547 }
548
549 static void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
550                 u8 avoid_flags, MapNode n, u8 or_flags)
551 {
552         for(s16 z=0; z<size.Z; z++)
553         for(s16 y=0; y<size.Y; y++)
554         for(s16 x=0; x<size.X; x++)
555         {
556                 v3s16 p = place + v3s16(x,y,z);
557                 if(vmanip.m_area.contains(p) == false)
558                         continue;
559                 u32 vi = vmanip.m_area.index(p);
560                 if(vmanip.m_flags[vi] & avoid_flags)
561                         continue;
562                 vmanip.m_flags[vi] |= or_flags;
563                 vmanip.m_data[vi] = n;
564         }
565 }
566
567 static void make_hole1(VoxelManipulator &vmanip, v3s16 place,
568                 INodeDefManager *ndef)
569 {
570         make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
571                         VMANIP_FLAG_DUNGEON_INSIDE);
572 }
573
574 static void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
575                 INodeDefManager *ndef)
576 {
577         make_hole1(vmanip, doorplace, ndef);
578         // Place torch (for testing)
579         //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(ndef->getId("mapgen_torch"));
580 }
581
582 static v3s16 rand_ortho_dir(PseudoRandom &random)
583 {
584         if(random.next()%2==0)
585                 return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
586         else
587                 return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
588 }
589
590 static v3s16 turn_xz(v3s16 olddir, int t)
591 {
592         v3s16 dir;
593         if(t == 0)
594         {
595                 // Turn right
596                 dir.X = olddir.Z;
597                 dir.Z = -olddir.X;
598                 dir.Y = olddir.Y;
599         }
600         else
601         {
602                 // Turn left
603                 dir.X = -olddir.Z;
604                 dir.Z = olddir.X;
605                 dir.Y = olddir.Y;
606         }
607         return dir;
608 }
609
610 static v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
611 {
612         int turn = random.range(0,2);
613         v3s16 dir;
614         if(turn == 0)
615         {
616                 // Go straight
617                 dir = olddir;
618         }
619         else if(turn == 1)
620                 // Turn right
621                 dir = turn_xz(olddir, 0);
622         else
623                 // Turn left
624                 dir = turn_xz(olddir, 1);
625         return dir;
626 }
627
628 static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace,
629                 v3s16 doordir, v3s16 &result_place, v3s16 &result_dir,
630                 PseudoRandom &random, INodeDefManager *ndef)
631 {
632         make_hole1(vmanip, doorplace, ndef);
633         v3s16 p0 = doorplace;
634         v3s16 dir = doordir;
635         u32 length;
636         if(random.next()%2)
637                 length = random.range(1,13);
638         else
639                 length = random.range(1,6);
640         length = random.range(1,13);
641         u32 partlength = random.range(1,13);
642         u32 partcount = 0;
643         s16 make_stairs = 0;
644         if(random.next()%2 == 0 && partlength >= 3)
645                 make_stairs = random.next()%2 ? 1 : -1;
646         for(u32 i=0; i<length; i++)
647         {
648                 v3s16 p = p0 + dir;
649                 if(partcount != 0)
650                         p.Y += make_stairs;
651
652                 /*// If already empty
653                 if(vmanip.getNodeNoExNoEmerge(p).getContent()
654                                 == CONTENT_AIR
655                 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
656                                 == CONTENT_AIR)
657                 {
658                 }*/
659
660                 if(vmanip.m_area.contains(p) == true
661                                 && vmanip.m_area.contains(p+v3s16(0,1,0)) == true)
662                 {
663                         if(make_stairs)
664                         {
665                                 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
666                                                 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0);
667                                 make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
668                                                 VMANIP_FLAG_DUNGEON_INSIDE);
669                                 make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
670                                                 VMANIP_FLAG_DUNGEON_INSIDE);
671                         }
672                         else
673                         {
674                                 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
675                                                 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0);
676                                 make_hole1(vmanip, p, ndef);
677                                 /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
678                                                 VMANIP_FLAG_DUNGEON_INSIDE);*/
679                         }
680
681                         p0 = p;
682                 }
683                 else
684                 {
685                         // Can't go here, turn away
686                         dir = turn_xz(dir, random.range(0,1));
687                         make_stairs = -make_stairs;
688                         partcount = 0;
689                         partlength = random.range(1,length);
690                         continue;
691                 }
692
693                 partcount++;
694                 if(partcount >= partlength)
695                 {
696                         partcount = 0;
697
698                         dir = random_turn(random, dir);
699
700                         partlength = random.range(1,length);
701
702                         make_stairs = 0;
703                         if(random.next()%2 == 0 && partlength >= 3)
704                                 make_stairs = random.next()%2 ? 1 : -1;
705                 }
706         }
707         result_place = p0;
708         result_dir = dir;
709 }
710
711 class RoomWalker
712 {
713 public:
714
715         RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
716                         INodeDefManager *ndef):
717                         vmanip(vmanip_),
718                         m_pos(pos),
719                         m_random(random),
720                         m_ndef(ndef)
721         {
722                 randomizeDir();
723         }
724
725         void randomizeDir()
726         {
727                 m_dir = rand_ortho_dir(m_random);
728         }
729
730         void setPos(v3s16 pos)
731         {
732                 m_pos = pos;
733         }
734
735         void setDir(v3s16 dir)
736         {
737                 m_dir = dir;
738         }
739
740         bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
741         {
742                 for(u32 i=0; i<100; i++)
743                 {
744                         v3s16 p = m_pos + m_dir;
745                         v3s16 p1 = p + v3s16(0,1,0);
746                         if(vmanip.m_area.contains(p) == false
747                                         || vmanip.m_area.contains(p1) == false
748                                         || i % 4 == 0)
749                         {
750                                 randomizeDir();
751                                 continue;
752                         }
753                         if(vmanip.getNodeNoExNoEmerge(p).getContent()
754                                         == m_ndef->getId("mapgen_cobble")
755                         && vmanip.getNodeNoExNoEmerge(p1).getContent()
756                                         == m_ndef->getId("mapgen_cobble"))
757                         {
758                                 // Found wall, this is a good place!
759                                 result_place = p;
760                                 result_dir = m_dir;
761                                 // Randomize next direction
762                                 randomizeDir();
763                                 return true;
764                         }
765                         /*
766                                 Determine where to move next
767                         */
768                         // Jump one up if the actual space is there
769                         if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
770                                         == m_ndef->getId("mapgen_cobble")
771                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
772                                         == CONTENT_AIR
773                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent()
774                                         == CONTENT_AIR)
775                                 p += v3s16(0,1,0);
776                         // Jump one down if the actual space is there
777                         if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
778                                         == m_ndef->getId("mapgen_cobble")
779                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent()
780                                         == CONTENT_AIR
781                         && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent()
782                                         == CONTENT_AIR)
783                                 p += v3s16(0,-1,0);
784                         // Check if walking is now possible
785                         if(vmanip.getNodeNoExNoEmerge(p).getContent()
786                                         != CONTENT_AIR
787                         || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent()
788                                         != CONTENT_AIR)
789                         {
790                                 // Cannot continue walking here
791                                 randomizeDir();
792                                 continue;
793                         }
794                         // Move there
795                         m_pos = p;
796                 }
797                 return false;
798         }
799
800         bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
801                         v3s16 &result_doordir, v3s16 &result_roomplace)
802         {
803                 for(s16 trycount=0; trycount<30; trycount++)
804                 {
805                         v3s16 doorplace;
806                         v3s16 doordir;
807                         bool r = findPlaceForDoor(doorplace, doordir);
808                         if(r == false)
809                                 continue;
810                         v3s16 roomplace;
811                         // X east, Z north, Y up
812 #if 1
813                         if(doordir == v3s16(1,0,0)) // X+
814                                 roomplace = doorplace +
815                                                 v3s16(0,-1,m_random.range(-roomsize.Z+2,-2));
816                         if(doordir == v3s16(-1,0,0)) // X-
817                                 roomplace = doorplace +
818                                                 v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2));
819                         if(doordir == v3s16(0,0,1)) // Z+
820                                 roomplace = doorplace +
821                                                 v3s16(m_random.range(-roomsize.X+2,-2),-1,0);
822                         if(doordir == v3s16(0,0,-1)) // Z-
823                                 roomplace = doorplace +
824                                                 v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1);
825 #endif
826 #if 0
827                         if(doordir == v3s16(1,0,0)) // X+
828                                 roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2);
829                         if(doordir == v3s16(-1,0,0)) // X-
830                                 roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2);
831                         if(doordir == v3s16(0,0,1)) // Z+
832                                 roomplace = doorplace + v3s16(-roomsize.X/2,-1,0);
833                         if(doordir == v3s16(0,0,-1)) // Z-
834                                 roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1);
835 #endif
836
837                         // Check fit
838                         bool fits = true;
839                         for(s16 z=1; z<roomsize.Z-1; z++)
840                         for(s16 y=1; y<roomsize.Y-1; y++)
841                         for(s16 x=1; x<roomsize.X-1; x++)
842                         {
843                                 v3s16 p = roomplace + v3s16(x,y,z);
844                                 if(vmanip.m_area.contains(p) == false)
845                                 {
846                                         fits = false;
847                                         break;
848                                 }
849                                 if(vmanip.m_flags[vmanip.m_area.index(p)]
850                                                 & VMANIP_FLAG_DUNGEON_INSIDE)
851                                 {
852                                         fits = false;
853                                         break;
854                                 }
855                         }
856                         if(fits == false)
857                         {
858                                 // Find new place
859                                 continue;
860                         }
861                         result_doorplace = doorplace;
862                         result_doordir = doordir;
863                         result_roomplace = roomplace;
864                         return true;
865                 }
866                 return false;
867         }
868
869 private:
870         VoxelManipulator &vmanip;
871         v3s16 m_pos;
872         v3s16 m_dir;
873         PseudoRandom &m_random;
874         INodeDefManager *m_ndef;
875 };
876
877 static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random,
878                 INodeDefManager *ndef)
879 {
880         v3s16 areasize = vmanip.m_area.getExtent();
881         v3s16 roomsize;
882         v3s16 roomplace;
883
884         /*
885                 Find place for first room
886         */
887         bool fits = false;
888         for(u32 i=0; i<100; i++)
889         {
890                 roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
891                 roomplace = vmanip.m_area.MinEdge + v3s16(
892                                 random.range(0,areasize.X-roomsize.X-1),
893                                 random.range(0,areasize.Y-roomsize.Y-1),
894                                 random.range(0,areasize.Z-roomsize.Z-1));
895                 /*
896                         Check that we're not putting the room to an unknown place,
897                         otherwise it might end up floating in the air
898                 */
899                 fits = true;
900                 for(s16 z=1; z<roomsize.Z-1; z++)
901                 for(s16 y=1; y<roomsize.Y-1; y++)
902                 for(s16 x=1; x<roomsize.X-1; x++)
903                 {
904                         v3s16 p = roomplace + v3s16(x,y,z);
905                         u32 vi = vmanip.m_area.index(p);
906                         if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
907                         {
908                                 fits = false;
909                                 break;
910                         }
911                         if(vmanip.m_data[vi].getContent() == CONTENT_IGNORE)
912                         {
913                                 fits = false;
914                                 break;
915                         }
916                 }
917                 if(fits)
918                         break;
919         }
920         // No place found
921         if(fits == false)
922                 return;
923
924         /*
925                 Stores the center position of the last room made, so that
926                 a new corridor can be started from the last room instead of
927                 the new room, if chosen so.
928         */
929         v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2);
930
931         u32 room_count = random.range(2,7);
932         for(u32 i=0; i<room_count; i++)
933         {
934                 // Make a room to the determined place
935                 make_room1(vmanip, roomsize, roomplace, ndef);
936
937                 v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2);
938
939                 // Place torch at room center (for testing)
940                 //vmanip.m_data[vmanip.m_area.index(room_center)] = MapNode(ndef->getId("mapgen_torch"));
941
942                 // Quit if last room
943                 if(i == room_count-1)
944                         break;
945
946                 // Determine walker start position
947
948                 bool start_in_last_room = (random.range(0,2)!=0);
949                 //bool start_in_last_room = true;
950
951                 v3s16 walker_start_place;
952
953                 if(start_in_last_room)
954                 {
955                         walker_start_place = last_room_center;
956                 }
957                 else
958                 {
959                         walker_start_place = room_center;
960                         // Store center of current room as the last one
961                         last_room_center = room_center;
962                 }
963
964                 // Create walker and find a place for a door
965                 RoomWalker walker(vmanip, walker_start_place, random, ndef);
966                 v3s16 doorplace;
967                 v3s16 doordir;
968                 bool r = walker.findPlaceForDoor(doorplace, doordir);
969                 if(r == false)
970                         return;
971
972                 if(random.range(0,1)==0)
973                         // Make the door
974                         make_door1(vmanip, doorplace, doordir, ndef);
975                 else
976                         // Don't actually make a door
977                         doorplace -= doordir;
978
979                 // Make a random corridor starting from the door
980                 v3s16 corridor_end;
981                 v3s16 corridor_end_dir;
982                 make_corridor(vmanip, doorplace, doordir, corridor_end,
983                                 corridor_end_dir, random, ndef);
984
985                 // Find a place for a random sized room
986                 roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
987                 walker.setPos(corridor_end);
988                 walker.setDir(corridor_end_dir);
989                 r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
990                 if(r == false)
991                         return;
992
993                 if(random.range(0,1)==0)
994                         // Make the door
995                         make_door1(vmanip, doorplace, doordir, ndef);
996                 else
997                         // Don't actually make a door
998                         roomplace -= doordir;
999
1000         }
1001 }
1002 #endif
1003
1004 #if 0
1005 static void make_nc(VoxelManipulator &vmanip, PseudoRandom &random,
1006                 INodeDefManager *ndef)
1007 {
1008         v3s16 dir;
1009         u8 facedir_i = 0;
1010         s32 r = random.range(0, 3);
1011         if(r == 0){
1012                 dir = v3s16( 1, 0, 0);
1013                 facedir_i = 3;
1014         }
1015         if(r == 1){
1016                 dir = v3s16(-1, 0, 0);
1017                 facedir_i = 1;
1018         }
1019         if(r == 2){
1020                 dir = v3s16( 0, 0, 1);
1021                 facedir_i = 2;
1022         }
1023         if(r == 3){
1024                 dir = v3s16( 0, 0,-1);
1025                 facedir_i = 0;
1026         }
1027         v3s16 p = vmanip.m_area.MinEdge + v3s16(
1028                         16+random.range(0,15),
1029                         16+random.range(0,15),
1030                         16+random.range(0,15));
1031         vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat"), facedir_i);
1032         u32 length = random.range(3,15);
1033         for(u32 j=0; j<length; j++)
1034         {
1035                 p -= dir;
1036                 vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat_rainbow"));
1037         }
1038 }
1039 #endif
1040
1041 /*
1042         Noise functions. Make sure seed is mangled differently in each one.
1043 */
1044
1045 #if 0
1046 /*
1047         Scaling the output of the noise function affects the overdrive of the
1048         contour function, which affects the shape of the output considerably.
1049 */
1050 #define CAVE_NOISE_SCALE 12.0
1051 //#define CAVE_NOISE_SCALE 10.0
1052 //#define CAVE_NOISE_SCALE 7.5
1053 //#define CAVE_NOISE_SCALE 5.0
1054 //#define CAVE_NOISE_SCALE 1.0
1055
1056 //#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
1057 #define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE)
1058
1059 NoiseParams get_cave_noise1_params(u64 seed)
1060 {
1061         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
1062                         200, CAVE_NOISE_SCALE);*/
1063         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
1064                         100, CAVE_NOISE_SCALE);*/
1065         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6,
1066                         100, CAVE_NOISE_SCALE);*/
1067         /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3,
1068                         100, CAVE_NOISE_SCALE);*/
1069         return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5,
1070                         50, CAVE_NOISE_SCALE);
1071         //return NoiseParams(NOISE_CONSTANT_ONE);
1072 }
1073
1074 NoiseParams get_cave_noise2_params(u64 seed)
1075 {
1076         /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
1077                         200, CAVE_NOISE_SCALE);*/
1078         /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
1079                         100, CAVE_NOISE_SCALE);*/
1080         /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3,
1081                         100, CAVE_NOISE_SCALE);*/
1082         return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5,
1083                         50, CAVE_NOISE_SCALE);
1084         //return NoiseParams(NOISE_CONSTANT_ONE);
1085 }
1086
1087 NoiseParams get_ground_noise1_params(u64 seed)
1088 {
1089         return NoiseParams(NOISE_PERLIN, seed+983240, 4,
1090                         0.55, 80.0, 40.0);
1091 }
1092
1093 NoiseParams get_ground_crumbleness_params(u64 seed)
1094 {
1095         return NoiseParams(NOISE_PERLIN, seed+34413, 3,
1096                         1.3, 20.0, 1.0);
1097 }
1098
1099 NoiseParams get_ground_wetness_params(u64 seed)
1100 {
1101         return NoiseParams(NOISE_PERLIN, seed+32474, 4,
1102                         1.1, 40.0, 1.0);
1103 }
1104
1105 bool is_cave(u64 seed, v3s16 p)
1106 {
1107         double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
1108         double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
1109         return d1*d2 > CAVE_NOISE_THRESHOLD;
1110 }
1111
1112 /*
1113         Ground density noise shall be interpreted by using this.
1114
1115         TODO: No perlin noises here, they should be outsourced
1116               and buffered
1117                   NOTE: The speed of these actually isn't terrible
1118 */
1119 bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
1120 {
1121         //return ((double)p.Y < ground_noise1_val);
1122
1123         double f = 0.55 + noise2d_perlin(
1124                         0.5+(float)p.X/250, 0.5+(float)p.Z/250,
1125                         seed+920381, 3, 0.45);
1126         if(f < 0.01)
1127                 f = 0.01;
1128         else if(f >= 1.0)
1129                 f *= 1.6;
1130         double h = WATER_LEVEL + 10 * noise2d_perlin(
1131                         0.5+(float)p.X/250, 0.5+(float)p.Z/250,
1132                         seed+84174, 4, 0.5);
1133         /*double f = 1;
1134         double h = 0;*/
1135         return ((double)p.Y - h < ground_noise1_val * f);
1136 }
1137
1138 /*
1139         Queries whether a position is ground or not.
1140 */
1141 bool is_ground(u64 seed, v3s16 p)
1142 {
1143         double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z);
1144         return val_is_ground(val1, p, seed);
1145 }
1146 #endif
1147
1148 // Amount of trees per area in nodes
1149 double tree_amount_2d(u64 seed, v2s16 p)
1150 {
1151         /*double noise = noise2d_perlin(
1152                         0.5+(float)p.X/250, 0.5+(float)p.Y/250,
1153                         seed+2, 5, 0.66);*/
1154         double noise = noise2d_perlin(
1155                         0.5+(float)p.X/125, 0.5+(float)p.Y/125,
1156                         seed+2, 4, 0.66);
1157         double zeroval = -0.39;
1158         if(noise < zeroval)
1159                 return 0;
1160         else
1161                 return 0.04 * (noise-zeroval) / (1.0-zeroval);
1162 }
1163
1164 #if 0
1165 double surface_humidity_2d(u64 seed, v2s16 p)
1166 {
1167         double noise = noise2d_perlin(
1168                         0.5+(float)p.X/500, 0.5+(float)p.Y/500,
1169                         seed+72384, 4, 0.66);
1170         noise = (noise + 1.0)/2.0;
1171         if(noise < 0.0)
1172                 noise = 0.0;
1173         if(noise > 1.0)
1174                 noise = 1.0;
1175         return noise;
1176 }
1177
1178 /*
1179         Incrementally find ground level from 3d noise
1180 */
1181 s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
1182 {
1183         // Start a bit fuzzy to make averaging lower precision values
1184         // more useful
1185         s16 level = myrand_range(-precision/2, precision/2);
1186         s16 dec[] = {31000, 100, 20, 4, 1, 0};
1187         s16 i;
1188         for(i = 1; dec[i] != 0 && precision <= dec[i]; i++)
1189         {
1190                 // First find non-ground by going upwards
1191                 // Don't stop in caves.
1192                 {
1193                         s16 max = level+dec[i-1]*2;
1194                         v3s16 p(p2d.X, level, p2d.Y);
1195                         for(; p.Y < max; p.Y += dec[i])
1196                         {
1197                                 if(!is_ground(seed, p))
1198                                 {
1199                                         level = p.Y;
1200                                         break;
1201                                 }
1202                         }
1203                 }
1204                 // Then find ground by going downwards from there.
1205                 // Go in caves, too, when precision is 1.
1206                 {
1207                         s16 min = level-dec[i-1]*2;
1208                         v3s16 p(p2d.X, level, p2d.Y);
1209                         for(; p.Y>min; p.Y-=dec[i])
1210                         {
1211                                 bool ground = is_ground(seed, p);
1212                                 /*if(dec[i] == 1 && is_cave(seed, p))
1213                                         ground = false;*/
1214                                 if(ground)
1215                                 {
1216                                         level = p.Y;
1217                                         break;
1218                                 }
1219                         }
1220                 }
1221         }
1222
1223         // This is more like the actual ground level
1224         level += dec[i-1]/2;
1225
1226         return level;
1227 }
1228
1229 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4);
1230
1231 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p)
1232 {
1233         v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
1234         v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
1235         double a = 0;
1236         a += find_ground_level_from_noise(seed,
1237                         v2s16(node_min.X, node_min.Y), p);
1238         a += find_ground_level_from_noise(seed,
1239                         v2s16(node_min.X, node_max.Y), p);
1240         a += find_ground_level_from_noise(seed,
1241                         v2s16(node_max.X, node_max.Y), p);
1242         a += find_ground_level_from_noise(seed,
1243                         v2s16(node_max.X, node_min.Y), p);
1244         a += find_ground_level_from_noise(seed,
1245                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p);
1246         a /= 5;
1247         return a;
1248 }
1249
1250 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
1251
1252 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
1253 {
1254         v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
1255         v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
1256         double a = -31000;
1257         // Corners
1258         a = MYMAX(a, find_ground_level_from_noise(seed,
1259                         v2s16(node_min.X, node_min.Y), p));
1260         a = MYMAX(a, find_ground_level_from_noise(seed,
1261                         v2s16(node_min.X, node_max.Y), p));
1262         a = MYMAX(a, find_ground_level_from_noise(seed,
1263                         v2s16(node_max.X, node_max.Y), p));
1264         a = MYMAX(a, find_ground_level_from_noise(seed,
1265                         v2s16(node_min.X, node_min.Y), p));
1266         // Center
1267         a = MYMAX(a, find_ground_level_from_noise(seed,
1268                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
1269         // Side middle points
1270         a = MYMAX(a, find_ground_level_from_noise(seed,
1271                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
1272         a = MYMAX(a, find_ground_level_from_noise(seed,
1273                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
1274         a = MYMAX(a, find_ground_level_from_noise(seed,
1275                         v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1276         a = MYMAX(a, find_ground_level_from_noise(seed,
1277                         v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1278         return a;
1279 }
1280
1281 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
1282
1283 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
1284 {
1285         v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
1286         v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
1287         double a = 31000;
1288         // Corners
1289         a = MYMIN(a, find_ground_level_from_noise(seed,
1290                         v2s16(node_min.X, node_min.Y), p));
1291         a = MYMIN(a, find_ground_level_from_noise(seed,
1292                         v2s16(node_min.X, node_max.Y), p));
1293         a = MYMIN(a, find_ground_level_from_noise(seed,
1294                         v2s16(node_max.X, node_max.Y), p));
1295         a = MYMIN(a, find_ground_level_from_noise(seed,
1296                         v2s16(node_min.X, node_min.Y), p));
1297         // Center
1298         a = MYMIN(a, find_ground_level_from_noise(seed,
1299                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
1300         // Side middle points
1301         a = MYMIN(a, find_ground_level_from_noise(seed,
1302                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p));
1303         a = MYMIN(a, find_ground_level_from_noise(seed,
1304                         v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p));
1305         a = MYMIN(a, find_ground_level_from_noise(seed,
1306                         v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1307         a = MYMIN(a, find_ground_level_from_noise(seed,
1308                         v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p));
1309         return a;
1310 }
1311 #endif
1312
1313 // Required by mapgen.h
1314 bool block_is_underground(u64 seed, v3s16 blockpos)
1315 {
1316         /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
1317                         seed, v2s16(blockpos.X, blockpos.Z));*/
1318         // Nah, this is just a heuristic, just return something
1319         s16 minimum_groundlevel = WATER_LEVEL;
1320
1321         if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
1322                 return true;
1323         else
1324                 return false;
1325 }
1326
1327 #define AVERAGE_MUD_AMOUNT 4
1328
1329 double base_rock_level_2d(u64 seed, v2s16 p)
1330 {
1331         // The base ground level
1332         double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
1333                         + 20. * noise2d_perlin(
1334                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1335                         seed+82341, 5, 0.6);
1336
1337         /*// A bit hillier one
1338         double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
1339                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1340                         seed+93413, 6, 0.69);
1341         if(base2 > base)
1342                 base = base2;*/
1343 #if 1
1344         // Higher ground level
1345         double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin(
1346                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1347                         seed+85039, 5, 0.6);
1348         //higher = 30; // For debugging
1349
1350         // Limit higher to at least base
1351         if(higher < base)
1352                 higher = base;
1353
1354         // Steepness factor of cliffs
1355         double b = 0.85 + 0.5 * noise2d_perlin(
1356                         0.5+(float)p.X/125., 0.5+(float)p.Y/125.,
1357                         seed-932, 5, 0.7);
1358         b = rangelim(b, 0.0, 1000.0);
1359         b = b*b*b*b*b*b*b;
1360         b *= 5;
1361         b = rangelim(b, 0.5, 1000.0);
1362         // Values 1.5...100 give quite horrible looking slopes
1363         if(b > 1.5 && b < 100.0){
1364                 if(b < 10.0)
1365                         b = 1.5;
1366                 else
1367                         b = 100.0;
1368         }
1369         //dstream<<"b="<<b<<std::endl;
1370         //double b = 20;
1371         //b = 0.25;
1372
1373         // Offset to more low
1374         double a_off = -0.20;
1375         // High/low selector
1376         /*double a = 0.5 + b * (a_off + noise2d_perlin(
1377                         0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
1378                         seed+4213, 6, 0.7));*/
1379         double a = (double)0.5 + b * (a_off + noise2d_perlin(
1380                         0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
1381                         seed+4213, 5, 0.69));
1382         // Limit
1383         a = rangelim(a, 0.0, 1.0);
1384
1385         //dstream<<"a="<<a<<std::endl;
1386
1387         double h = base*(1.0-a) + higher*a;
1388 #else
1389         double h = base;
1390 #endif
1391         return h;
1392 }
1393
1394 s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
1395 {
1396         return base_rock_level_2d(seed, p2d) + AVERAGE_MUD_AMOUNT;
1397 }
1398
1399 double get_mud_add_amount(u64 seed, v2s16 p)
1400 {
1401         return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
1402                         0.5+(float)p.X/200, 0.5+(float)p.Y/200,
1403                         seed+91013, 3, 0.55));
1404 }
1405
1406 bool get_have_beach(u64 seed, v2s16 p2d)
1407 {
1408         // Determine whether to have sand here
1409         double sandnoise = noise2d_perlin(
1410                         0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
1411                         seed+59420, 3, 0.50);
1412
1413         return (sandnoise > 0.15);
1414 }
1415
1416 enum BiomeType
1417 {
1418         BT_NORMAL,
1419         BT_DESERT
1420 };
1421
1422 BiomeType get_biome(u64 seed, v2s16 p2d)
1423 {
1424         // Just do something very simple as for now
1425         double d = noise2d_perlin(
1426                         0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
1427                         seed+9130, 3, 0.50);
1428         if(d > 0.45)
1429                 return BT_DESERT;
1430         if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0  )
1431                 return BT_DESERT;
1432         return BT_NORMAL;
1433 };
1434
1435 u32 get_blockseed(u64 seed, v3s16 p)
1436 {
1437         s32 x=p.X, y=p.Y, z=p.Z;
1438         return (u32)(seed%0x100000000ULL) + z*38134234 + y*42123 + x*23;
1439 }
1440
1441 #define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
1442
1443 void make_block(BlockMakeData *data)
1444 {
1445         if(data->no_op)
1446         {
1447                 //dstream<<"makeBlock: no-op"<<std::endl;
1448                 return;
1449         }
1450
1451         assert(data->vmanip);
1452         assert(data->nodedef);
1453         assert(data->blockpos_requested.X >= data->blockpos_min.X &&
1454                         data->blockpos_requested.Y >= data->blockpos_min.Y &&
1455                         data->blockpos_requested.Z >= data->blockpos_min.Z);
1456         assert(data->blockpos_requested.X <= data->blockpos_max.X &&
1457                         data->blockpos_requested.Y <= data->blockpos_max.Y &&
1458                         data->blockpos_requested.Z <= data->blockpos_max.Z);
1459
1460         INodeDefManager *ndef = data->nodedef;
1461
1462         // Hack: use minimum block coordinates for old code that assumes
1463         // a single block
1464         v3s16 blockpos = data->blockpos_requested;
1465
1466         /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
1467                         <<blockpos.Z<<")"<<std::endl;*/
1468
1469         v3s16 blockpos_min = data->blockpos_min;
1470         v3s16 blockpos_max = data->blockpos_max;
1471         v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1);
1472         v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1);
1473
1474         ManualMapVoxelManipulator &vmanip = *(data->vmanip);
1475         // Area of central chunk
1476         v3s16 node_min = blockpos_min*MAP_BLOCKSIZE;
1477         v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
1478         // Full allocated area
1479         v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
1480         v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
1481
1482         v3s16 central_area_size = node_max - node_min + v3s16(1,1,1);
1483
1484         const s16 max_spread_amount = MAP_BLOCKSIZE;
1485
1486         int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
1487                         * (blockpos_max.Y - blockpos_min.Y + 1)
1488                         * (blockpos_max.Z - blockpos_max.Z + 1);
1489
1490         int volume_nodes = volume_blocks *
1491                         MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1492
1493         // Generated surface area
1494         //double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume;
1495
1496         // Horribly wrong heuristic, but better than nothing
1497         bool block_is_underground = (WATER_LEVEL > node_max.Y);
1498
1499         /*
1500                 Create a block-specific seed
1501         */
1502         u32 blockseed = get_blockseed(data->seed, full_node_min);
1503
1504         /*
1505                 Cache some ground type values for speed
1506         */
1507
1508 // Creates variables c_name=id and n_name=node
1509 #define CONTENT_VARIABLE(ndef, name)\
1510         content_t c_##name = ndef->getId("mapgen_" #name);\
1511         MapNode n_##name(c_##name);
1512 // Default to something else if was CONTENT_IGNORE
1513 #define CONTENT_VARIABLE_FALLBACK(name, dname)\
1514         if(c_##name == CONTENT_IGNORE){\
1515                 c_##name = c_##dname;\
1516                 n_##name = n_##dname;\
1517         }
1518
1519         CONTENT_VARIABLE(ndef, stone);
1520         CONTENT_VARIABLE(ndef, air);
1521         CONTENT_VARIABLE(ndef, water_source);
1522         CONTENT_VARIABLE(ndef, dirt);
1523         CONTENT_VARIABLE(ndef, sand);
1524         CONTENT_VARIABLE(ndef, gravel);
1525         CONTENT_VARIABLE(ndef, clay);
1526         CONTENT_VARIABLE(ndef, lava_source);
1527         CONTENT_VARIABLE(ndef, cobble);
1528         CONTENT_VARIABLE(ndef, mossycobble);
1529         CONTENT_VARIABLE(ndef, dirt_with_grass);
1530         CONTENT_VARIABLE(ndef, junglegrass);
1531         CONTENT_VARIABLE(ndef, stone_with_coal);
1532         CONTENT_VARIABLE(ndef, stone_with_iron);
1533         CONTENT_VARIABLE(ndef, mese);
1534         CONTENT_VARIABLE(ndef, desert_sand);
1535         CONTENT_VARIABLE_FALLBACK(desert_sand, sand);
1536         CONTENT_VARIABLE(ndef, desert_stone);
1537         CONTENT_VARIABLE_FALLBACK(desert_stone, stone);
1538
1539         // Maximum height of the stone surface and obstacles.
1540         // This is used to guide the cave generation
1541         s16 stone_surface_max_y = 0;
1542
1543         /*
1544                 Generate general ground level to full area
1545         */
1546         {
1547 #if 1
1548         TimeTaker timer1("Generating ground level");
1549
1550         for(s16 x=node_min.X; x<=node_max.X; x++)
1551         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1552         {
1553                 // Node position
1554                 v2s16 p2d = v2s16(x,z);
1555
1556                 /*
1557                         Skip of already generated
1558                 */
1559                 /*{
1560                         v3s16 p(p2d.X, node_min.Y, p2d.Y);
1561                         if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR)
1562                                 continue;
1563                 }*/
1564
1565                 // Ground height at this point
1566                 float surface_y_f = 0.0;
1567
1568                 // Use perlin noise for ground height
1569                 surface_y_f = base_rock_level_2d(data->seed, p2d);
1570
1571                 /*// Experimental stuff
1572                 {
1573                         float a = highlands_level_2d(data->seed, p2d);
1574                         if(a > surface_y_f)
1575                                 surface_y_f = a;
1576                 }*/
1577
1578                 // Convert to integer
1579                 s16 surface_y = (s16)surface_y_f;
1580
1581                 // Log it
1582                 if(surface_y > stone_surface_max_y)
1583                         stone_surface_max_y = surface_y;
1584
1585                 BiomeType bt = get_biome(data->seed, p2d);
1586                 /*
1587                         Fill ground with stone
1588                 */
1589                 {
1590                         // Use fast index incrementing
1591                         v3s16 em = vmanip.m_area.getExtent();
1592                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
1593                         for(s16 y=node_min.Y; y<=node_max.Y; y++)
1594                         {
1595                                 if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){
1596                                         if(y <= surface_y){
1597                                                 if(y > WATER_LEVEL && bt == BT_DESERT)
1598                                                         vmanip.m_data[i] = n_desert_stone;
1599                                                 else
1600                                                         vmanip.m_data[i] = n_stone;
1601                                         } else if(y <= WATER_LEVEL){
1602                                                 vmanip.m_data[i] = MapNode(c_water_source);
1603                                         } else {
1604                                                 vmanip.m_data[i] = MapNode(c_air);
1605                                         }
1606                                 }
1607                                 vmanip.m_area.add_y(em, i, 1);
1608                         }
1609                 }
1610         }
1611 #endif
1612
1613         }//timer1
1614
1615         // Limit dirt flow area by 1 because mud is flown into neighbors.
1616         assert(central_area_size.X == central_area_size.Z);
1617         s16 mudflow_minpos = 0-max_spread_amount+1;
1618         s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2;
1619
1620         /*
1621                 Loop this part, it will make stuff look older and newer nicely
1622         */
1623
1624         const u32 age_loops = 2;
1625         for(u32 i_age=0; i_age<age_loops; i_age++)
1626         { // Aging loop
1627         /******************************
1628                 BEGINNING OF AGING LOOP
1629         ******************************/
1630
1631 #if 1
1632         {
1633         // 24ms @cs=8
1634         //TimeTaker timer1("caves");
1635
1636         /*
1637                 Make caves (this code is relatively horrible)
1638         */
1639         double cave_amount = 6.0 + 6.0 * noise2d_perlin(
1640                         0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250,
1641                         data->seed+34329, 3, 0.50);
1642         cave_amount = MYMAX(0.0, cave_amount);
1643         u32 caves_count = cave_amount * volume_nodes / 50000;
1644         u32 bruises_count = 1;
1645         PseudoRandom ps(blockseed+21343);
1646         PseudoRandom ps2(blockseed+1032);
1647         if(ps.range(1, 6) == 1)
1648                 bruises_count = ps.range(0, ps.range(0, 2));
1649         if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_DESERT){
1650                 caves_count /= 3;
1651                 bruises_count /= 3;
1652         }
1653         for(u32 jj=0; jj<caves_count+bruises_count; jj++)
1654         {
1655                 bool large_cave = (jj >= caves_count);
1656                 s16 min_tunnel_diameter = 2;
1657                 s16 max_tunnel_diameter = ps.range(2,6);
1658                 int dswitchint = ps.range(1,14);
1659                 u16 tunnel_routepoints = 0;
1660                 int part_max_length_rs = 0;
1661                 if(large_cave){
1662                         part_max_length_rs = ps.range(2,4);
1663                         tunnel_routepoints = ps.range(5, ps.range(15,30));
1664                         min_tunnel_diameter = 5;
1665                         max_tunnel_diameter = ps.range(7, ps.range(8,24));
1666                 } else {
1667                         part_max_length_rs = ps.range(2,9);
1668                         tunnel_routepoints = ps.range(10, ps.range(15,30));
1669                 }
1670                 bool large_cave_is_flat = (ps.range(0,1) == 0);
1671
1672                 v3f main_direction(0,0,0);
1673
1674                 // Allowed route area size in nodes
1675                 v3s16 ar = central_area_size;
1676
1677                 // Area starting point in nodes
1678                 v3s16 of = node_min;
1679
1680                 // Allow a bit more
1681                 //(this should be more than the maximum radius of the tunnel)
1682                 //s16 insure = 5; // Didn't work with max_d = 20
1683                 s16 insure = 10;
1684                 s16 more = max_spread_amount - max_tunnel_diameter/2 - insure;
1685                 ar += v3s16(1,0,1) * more * 2;
1686                 of -= v3s16(1,0,1) * more;
1687
1688                 s16 route_y_min = 0;
1689                 // Allow half a diameter + 7 over stone surface
1690                 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
1691
1692                 /*// If caves, don't go through surface too often
1693                 if(large_cave == false)
1694                         route_y_max -= ps.range(0, max_tunnel_diameter*2);*/
1695
1696                 // Limit maximum to area
1697                 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
1698
1699                 if(large_cave)
1700                 {
1701                         /*// Minimum is at y=0
1702                         route_y_min = -of.Y - 0;*/
1703                         // Minimum is at y=max_tunnel_diameter/4
1704                         //route_y_min = -of.Y + max_tunnel_diameter/4;
1705                         //s16 min = -of.Y + max_tunnel_diameter/4;
1706                         //s16 min = -of.Y + 0;
1707                         s16 min = 0;
1708                         if(node_min.Y < WATER_LEVEL && node_max.Y > WATER_LEVEL)
1709                         {
1710                                 min = WATER_LEVEL - max_tunnel_diameter/3 - of.Y;
1711                                 route_y_max = WATER_LEVEL + max_tunnel_diameter/3 - of.Y;
1712                         }
1713                         route_y_min = ps.range(min, min + max_tunnel_diameter);
1714                         route_y_min = rangelim(route_y_min, 0, route_y_max);
1715                 }
1716
1717                 /*dstream<<"route_y_min = "<<route_y_min
1718                                 <<", route_y_max = "<<route_y_max<<std::endl;*/
1719
1720                 s16 route_start_y_min = route_y_min;
1721                 s16 route_start_y_max = route_y_max;
1722
1723                 // Start every 4th cave from surface when applicable
1724                 /*bool coming_from_surface = false;
1725                 if(node_min.Y <= 0 && node_max.Y >= 0){
1726                         coming_from_surface = (jj % 4 == 0 && large_cave == false);
1727                         if(coming_from_surface)
1728                                 route_start_y_min = -of.Y + stone_surface_max_y + 10;
1729                 }*/
1730
1731                 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
1732                 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
1733
1734                 // Randomize starting position
1735                 v3f orp(
1736                         (float)(ps.next()%ar.X)+0.5,
1737                         (float)(ps.range(route_start_y_min, route_start_y_max))+0.5,
1738                         (float)(ps.next()%ar.Z)+0.5
1739                 );
1740
1741                 v3s16 startp(orp.X, orp.Y, orp.Z);
1742                 startp += of;
1743
1744                 MapNode airnode(CONTENT_AIR);
1745                 MapNode waternode(c_water_source);
1746                 MapNode lavanode(c_lava_source);
1747
1748                 /*
1749                         Generate some tunnel starting from orp
1750                 */
1751
1752                 for(u16 j=0; j<tunnel_routepoints; j++)
1753                 {
1754                         if(j%dswitchint==0 && large_cave == false)
1755                         {
1756                                 main_direction = v3f(
1757                                         ((float)(ps.next()%20)-(float)10)/10,
1758                                         ((float)(ps.next()%20)-(float)10)/30,
1759                                         ((float)(ps.next()%20)-(float)10)/10
1760                                 );
1761                                 main_direction *= (float)ps.range(0, 10)/10;
1762                         }
1763
1764                         // Randomize size
1765                         s16 min_d = min_tunnel_diameter;
1766                         s16 max_d = max_tunnel_diameter;
1767                         s16 rs = ps.range(min_d, max_d);
1768
1769                         // Every second section is rough
1770                         bool randomize_xz = (ps2.range(1,2) == 1);
1771
1772                         v3s16 maxlen;
1773                         if(large_cave)
1774                         {
1775                                 maxlen = v3s16(
1776                                         rs*part_max_length_rs,
1777                                         rs*part_max_length_rs/2,
1778                                         rs*part_max_length_rs
1779                                 );
1780                         }
1781                         else
1782                         {
1783                                 maxlen = v3s16(
1784                                         rs*part_max_length_rs,
1785                                         ps.range(1, rs*part_max_length_rs),
1786                                         rs*part_max_length_rs
1787                                 );
1788                         }
1789
1790                         v3f vec;
1791
1792                         vec = v3f(
1793                                 (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
1794                                 (float)(ps.next()%(maxlen.Y*1))-(float)maxlen.Y/2,
1795                                 (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
1796                         );
1797
1798                         // Jump downward sometimes
1799                         if(!large_cave && ps.range(0,12) == 0)
1800                         {
1801                                 vec = v3f(
1802                                         (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2,
1803                                         (float)(ps.next()%(maxlen.Y*2))-(float)maxlen.Y*2/2,
1804                                         (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2
1805                                 );
1806                         }
1807
1808                         /*if(large_cave){
1809                                 v3f p = orp + vec;
1810                                 s16 h = find_ground_level_clever(vmanip,
1811                                                 v2s16(p.X, p.Z), ndef);
1812                                 route_y_min = h - rs/3;
1813                                 route_y_max = h + rs;
1814                         }*/
1815
1816                         vec += main_direction;
1817
1818                         v3f rp = orp + vec;
1819                         if(rp.X < 0)
1820                                 rp.X = 0;
1821                         else if(rp.X >= ar.X)
1822                                 rp.X = ar.X-1;
1823                         if(rp.Y < route_y_min)
1824                                 rp.Y = route_y_min;
1825                         else if(rp.Y >= route_y_max)
1826                                 rp.Y = route_y_max-1;
1827                         if(rp.Z < 0)
1828                                 rp.Z = 0;
1829                         else if(rp.Z >= ar.Z)
1830                                 rp.Z = ar.Z-1;
1831                         vec = rp - orp;
1832
1833                         for(float f=0; f<1.0; f+=1.0/vec.getLength())
1834                         {
1835                                 v3f fp = orp + vec * f;
1836                                 fp.X += 0.1*ps.range(-10,10);
1837                                 fp.Z += 0.1*ps.range(-10,10);
1838                                 v3s16 cp(fp.X, fp.Y, fp.Z);
1839
1840                                 s16 d0 = -rs/2;
1841                                 s16 d1 = d0 + rs;
1842                                 if(randomize_xz){
1843                                         d0 += ps.range(-1,1);
1844                                         d1 += ps.range(-1,1);
1845                                 }
1846                                 for(s16 z0=d0; z0<=d1; z0++)
1847                                 {
1848                                         s16 si = rs/2 - MYMAX(0, abs(z0)-rs/7-1);
1849                                         for(s16 x0=-si-ps.range(0,1); x0<=si-1+ps.range(0,1); x0++)
1850                                         {
1851                                                 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
1852                                                 s16 si2 = rs/2 - MYMAX(0, maxabsxz-rs/7-1);
1853                                                 for(s16 y0=-si2; y0<=si2; y0++)
1854                                                 {
1855                                                         /*// Make better floors in small caves
1856                                                         if(y0 <= -rs/2 && rs<=7)
1857                                                                 continue;*/
1858                                                         if(large_cave_is_flat){
1859                                                                 // Make large caves not so tall
1860                                                                 if(rs > 7 && abs(y0) >= rs/3)
1861                                                                         continue;
1862                                                         }
1863
1864                                                         s16 z = cp.Z + z0;
1865                                                         s16 y = cp.Y + y0;
1866                                                         s16 x = cp.X + x0;
1867                                                         v3s16 p(x,y,z);
1868                                                         p += of;
1869
1870                                                         if(vmanip.m_area.contains(p) == false)
1871                                                                 continue;
1872
1873                                                         u32 i = vmanip.m_area.index(p);
1874
1875                                                         if(large_cave)
1876                                                         {
1877                                                                 if(full_node_min.Y < WATER_LEVEL &&
1878                                                                         full_node_max.Y > WATER_LEVEL){
1879                                                                         if(p.Y <= WATER_LEVEL)
1880                                                                                 vmanip.m_data[i] = waternode;
1881                                                                         else
1882                                                                                 vmanip.m_data[i] = airnode;
1883                                                                 } else if(full_node_max.Y < WATER_LEVEL){
1884                                                                         if(p.Y < startp.Y - 2)
1885                                                                                 vmanip.m_data[i] = lavanode;
1886                                                                         else
1887                                                                                 vmanip.m_data[i] = airnode;
1888                                                                 } else {
1889                                                                         vmanip.m_data[i] = airnode;
1890                                                                 }
1891                                                         } else {
1892                                                                 // Don't replace air or water or lava or ignore
1893                                                                 if(vmanip.m_data[i].getContent() == CONTENT_IGNORE ||
1894                                                                 vmanip.m_data[i].getContent() == CONTENT_AIR ||
1895                                                                 vmanip.m_data[i].getContent() == c_water_source ||
1896                                                                 vmanip.m_data[i].getContent() == c_lava_source)
1897                                                                         continue;
1898
1899                                                                 vmanip.m_data[i] = airnode;
1900
1901                                                                 // Set tunnel flag
1902                                                                 vmanip.m_flags[i] |= VMANIP_FLAG_CAVE;
1903                                                         }
1904                                                 }
1905                                         }
1906                                 }
1907                         }
1908
1909                         orp = rp;
1910                 }
1911
1912         }
1913
1914         }//timer1
1915 #endif
1916
1917 #if 1
1918         {
1919         // 15ms @cs=8
1920         TimeTaker timer1("add mud");
1921
1922         /*
1923                 Add mud to the central chunk
1924         */
1925
1926         for(s16 x=node_min.X; x<=node_max.X; x++)
1927         for(s16 z=node_min.Z; z<=node_max.Z; z++)
1928         {
1929                 // Node position in 2d
1930                 v2s16 p2d = v2s16(x,z);
1931
1932                 // Randomize mud amount
1933                 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5;
1934
1935                 // Find ground level
1936                 s16 surface_y = find_stone_level(vmanip, p2d, ndef);
1937                 // Handle area not found
1938                 if(surface_y == vmanip.m_area.MinEdge.Y - 1)
1939                         continue;
1940
1941                 MapNode addnode(c_dirt);
1942                 BiomeType bt = get_biome(data->seed, p2d);
1943
1944                 if(bt == BT_DESERT)
1945                         addnode = MapNode(c_desert_sand);
1946
1947                 if(bt == BT_DESERT && surface_y + mud_add_amount <= WATER_LEVEL+1){
1948                         addnode = MapNode(c_sand);
1949                 } else if(mud_add_amount <= 0){
1950                         mud_add_amount = 1 - mud_add_amount;
1951                         addnode = MapNode(c_gravel);
1952                 } else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) &&
1953                                 surface_y + mud_add_amount <= WATER_LEVEL+2){
1954                         addnode = MapNode(c_sand);
1955                 }
1956
1957                 if(bt == BT_DESERT){
1958                         if(surface_y > 20){
1959                                 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5);
1960                         }
1961                 }
1962
1963                 /*
1964                         If topmost node is grass, change it to mud.
1965                         It might be if it was flown to there from a neighboring
1966                         chunk and then converted.
1967                 */
1968                 {
1969                         u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
1970                         MapNode *n = &vmanip.m_data[i];
1971                         if(n->getContent() == c_dirt_with_grass)
1972                                 *n = MapNode(c_dirt);
1973                 }
1974
1975                 /*
1976                         Add mud on ground
1977                 */
1978                 {
1979                         s16 mudcount = 0;
1980                         v3s16 em = vmanip.m_area.getExtent();
1981                         s16 y_start = surface_y+1;
1982                         u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
1983                         for(s16 y=y_start; y<=node_max.Y; y++)
1984                         {
1985                                 if(mudcount >= mud_add_amount)
1986                                         break;
1987
1988                                 MapNode &n = vmanip.m_data[i];
1989                                 n = addnode;
1990                                 mudcount++;
1991
1992                                 vmanip.m_area.add_y(em, i, 1);
1993                         }
1994                 }
1995
1996         }
1997
1998         }//timer1
1999 #endif
2000
2001         /*
2002                 Add blobs of dirt and gravel underground
2003         */
2004         if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_NORMAL)
2005         {
2006         PseudoRandom pr(blockseed+983);
2007         for(int i=0; i<volume_nodes/10/10/10; i++)
2008         {
2009                 bool only_fill_cave = (myrand_range(0,1) != 0);
2010                 v3s16 size(
2011                         pr.range(1, 8),
2012                         pr.range(1, 8),
2013                         pr.range(1, 8)
2014                 );
2015                 v3s16 p0(
2016                         pr.range(node_min.X, node_max.X)-size.X/2,
2017                         pr.range(node_min.Y, node_max.Y)-size.Y/2,
2018                         pr.range(node_min.Z, node_max.Z)-size.Z/2
2019                 );
2020                 MapNode n1;
2021                 if(p0.Y > -32 && pr.range(0,1) == 0)
2022                         n1 = MapNode(c_dirt);
2023                 else
2024                         n1 = MapNode(c_gravel);
2025                 for(int x1=0; x1<size.X; x1++)
2026                 for(int y1=0; y1<size.Y; y1++)
2027                 for(int z1=0; z1<size.Z; z1++)
2028                 {
2029                         v3s16 p = p0 + v3s16(x1,y1,z1);
2030                         u32 i = vmanip.m_area.index(p);
2031                         if(!vmanip.m_area.contains(i))
2032                                 continue;
2033                         // Cancel if not stone and not cave air
2034                         if(vmanip.m_data[i].getContent() != c_stone &&
2035                                         !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
2036                                 continue;
2037                         if(only_fill_cave && !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE))
2038                                 continue;
2039                         vmanip.m_data[i] = n1;
2040                 }
2041         }
2042         }
2043
2044 #if 1
2045         {
2046         // 340ms @cs=8
2047         TimeTaker timer1("flow mud");
2048
2049         /*
2050                 Flow mud away from steep edges
2051         */
2052
2053         // Iterate a few times
2054         for(s16 k=0; k<3; k++)
2055         {
2056
2057         for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++)
2058         for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++)
2059         {
2060                 // Invert coordinates every 2nd iteration
2061                 if(k%2 == 0)
2062                 {
2063                         x = mudflow_maxpos - (x-mudflow_minpos);
2064                         z = mudflow_maxpos - (z-mudflow_minpos);
2065                 }
2066
2067                 // Node position in 2d
2068                 v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z);
2069
2070                 v3s16 em = vmanip.m_area.getExtent();
2071                 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
2072                 s16 y=node_max.Y;
2073
2074                 while(y >= node_min.Y)
2075                 {
2076
2077                 for(;; y--)
2078                 {
2079                         MapNode *n = NULL;
2080                         // Find mud
2081                         for(; y>=node_min.Y; y--)
2082                         {
2083                                 n = &vmanip.m_data[i];
2084                                 //if(content_walkable(n->d))
2085                                 //      break;
2086                                 if(n->getContent() == c_dirt ||
2087                                                 n->getContent() == c_dirt_with_grass ||
2088                                                 n->getContent() == c_gravel)
2089                                         break;
2090
2091                                 vmanip.m_area.add_y(em, i, -1);
2092                         }
2093
2094                         // Stop if out of area
2095                         //if(vmanip.m_area.contains(i) == false)
2096                         if(y < node_min.Y)
2097                                 break;
2098
2099                         /*// If not mud, do nothing to it
2100                         MapNode *n = &vmanip.m_data[i];
2101                         if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2102                                 continue;*/
2103
2104                         if(n->getContent() == c_dirt ||
2105                                         n->getContent() == c_dirt_with_grass)
2106                         {
2107                                 // Make it exactly mud
2108                                 n->setContent(c_dirt);
2109
2110                                 /*
2111                                         Don't flow it if the stuff under it is not mud
2112                                 */
2113                                 {
2114                                         u32 i2 = i;
2115                                         vmanip.m_area.add_y(em, i2, -1);
2116                                         // Cancel if out of area
2117                                         if(vmanip.m_area.contains(i2) == false)
2118                                                 continue;
2119                                         MapNode *n2 = &vmanip.m_data[i2];
2120                                         if(n2->getContent() != c_dirt &&
2121                                                         n2->getContent() != c_dirt_with_grass)
2122                                                 continue;
2123                                 }
2124                         }
2125
2126                         /*s16 recurse_count = 0;
2127         mudflow_recurse:*/
2128
2129                         v3s16 dirs4[4] = {
2130                                 v3s16(0,0,1), // back
2131                                 v3s16(1,0,0), // right
2132                                 v3s16(0,0,-1), // front
2133                                 v3s16(-1,0,0), // left
2134                         };
2135
2136                         // Theck that upper is air or doesn't exist.
2137                         // Cancel dropping if upper keeps it in place
2138                         u32 i3 = i;
2139                         vmanip.m_area.add_y(em, i3, 1);
2140                         if(vmanip.m_area.contains(i3) == true
2141                                         && ndef->get(vmanip.m_data[i3]).walkable)
2142                         {
2143                                 continue;
2144                         }
2145
2146                         // Drop mud on side
2147
2148                         for(u32 di=0; di<4; di++)
2149                         {
2150                                 v3s16 dirp = dirs4[di];
2151                                 u32 i2 = i;
2152                                 // Move to side
2153                                 vmanip.m_area.add_p(em, i2, dirp);
2154                                 // Fail if out of area
2155                                 if(vmanip.m_area.contains(i2) == false)
2156                                         continue;
2157                                 // Check that side is air
2158                                 MapNode *n2 = &vmanip.m_data[i2];
2159                                 if(ndef->get(*n2).walkable)
2160                                         continue;
2161                                 // Check that under side is air
2162                                 vmanip.m_area.add_y(em, i2, -1);
2163                                 if(vmanip.m_area.contains(i2) == false)
2164                                         continue;
2165                                 n2 = &vmanip.m_data[i2];
2166                                 if(ndef->get(*n2).walkable)
2167                                         continue;
2168                                 /*// Check that under that is air (need a drop of 2)
2169                                 vmanip.m_area.add_y(em, i2, -1);
2170                                 if(vmanip.m_area.contains(i2) == false)
2171                                         continue;
2172                                 n2 = &vmanip.m_data[i2];
2173                                 if(content_walkable(n2->d))
2174                                         continue;*/
2175                                 // Loop further down until not air
2176                                 bool dropped_to_unknown = false;
2177                                 do{
2178                                         vmanip.m_area.add_y(em, i2, -1);
2179                                         n2 = &vmanip.m_data[i2];
2180                                         // if out of known area
2181                                         if(vmanip.m_area.contains(i2) == false
2182                                                         || n2->getContent() == CONTENT_IGNORE){
2183                                                 dropped_to_unknown = true;
2184                                                 break;
2185                                         }
2186                                 }while(ndef->get(*n2).walkable == false);
2187                                 // Loop one up so that we're in air
2188                                 vmanip.m_area.add_y(em, i2, 1);
2189                                 n2 = &vmanip.m_data[i2];
2190
2191                                 bool old_is_water = (n->getContent() == c_water_source);
2192                                 // Move mud to new place
2193                                 if(!dropped_to_unknown) {
2194                                         *n2 = *n;
2195                                         // Set old place to be air (or water)
2196                                         if(old_is_water)
2197                                                 *n = MapNode(c_water_source);
2198                                         else
2199                                                 *n = MapNode(CONTENT_AIR);
2200                                 }
2201
2202                                 // Done
2203                                 break;
2204                         }
2205                 }
2206                 }
2207         }
2208
2209         }
2210
2211         }//timer1
2212 #endif
2213
2214         } // Aging loop
2215         /***********************
2216                 END OF AGING LOOP
2217         ************************/
2218
2219         /*
2220                 Add top and bottom side of water to transforming_liquid queue
2221         */
2222
2223         for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
2224         for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
2225         {
2226                 // Node position
2227                 v2s16 p2d(x,z);
2228                 {
2229                         bool water_found = false;
2230                         // Use fast index incrementing
2231                         v3s16 em = vmanip.m_area.getExtent();
2232                         u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
2233                         for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
2234                         {
2235                                 if(y == full_node_max.Y){
2236                                         water_found =
2237                                                 (vmanip.m_data[i].getContent() == c_water_source ||
2238                                                 vmanip.m_data[i].getContent() == c_lava_source);
2239                                 }
2240                                 else if(water_found == false)
2241                                 {
2242                                         if(vmanip.m_data[i].getContent() == c_water_source ||
2243                                                         vmanip.m_data[i].getContent() == c_lava_source)
2244                                         {
2245                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2246                                                 data->transforming_liquid.push_back(p);
2247                                                 water_found = true;
2248                                         }
2249                                 }
2250                                 else
2251                                 {
2252                                         // This can be done because water_found can only
2253                                         // turn to true and end up here after going through
2254                                         // a single block.
2255                                         if(vmanip.m_data[i+1].getContent() != c_water_source ||
2256                                                         vmanip.m_data[i+1].getContent() != c_lava_source)
2257                                         {
2258                                                 v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
2259                                                 data->transforming_liquid.push_back(p);
2260                                                 water_found = false;
2261                                         }
2262                                 }
2263
2264                                 vmanip.m_area.add_y(em, i, -1);
2265                         }
2266                 }
2267         }
2268
2269         /*
2270                 Grow grass
2271         */
2272
2273         for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
2274         for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
2275         {
2276                 // Node position in 2d
2277                 v2s16 p2d = v2s16(x,z);
2278
2279                 /*
2280                         Find the lowest surface to which enough light ends up
2281                         to make grass grow.
2282
2283                         Basically just wait until not air and not leaves.
2284                 */
2285                 s16 surface_y = 0;
2286                 {
2287                         v3s16 em = vmanip.m_area.getExtent();
2288                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
2289                         s16 y;
2290                         // Go to ground level
2291                         for(y=node_max.Y; y>=full_node_min.Y; y--)
2292                         {
2293                                 MapNode &n = vmanip.m_data[i];
2294                                 if(ndef->get(n).param_type != CPT_LIGHT
2295                                                 || ndef->get(n).liquid_type != LIQUID_NONE)
2296                                         break;
2297                                 vmanip.m_area.add_y(em, i, -1);
2298                         }
2299                         if(y >= full_node_min.Y)
2300                                 surface_y = y;
2301                         else
2302                                 surface_y = full_node_min.Y;
2303                 }
2304
2305                 u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
2306                 MapNode *n = &vmanip.m_data[i];
2307                 if(n->getContent() == c_dirt){
2308                         // Well yeah, this can't be overground...
2309                         if(surface_y < WATER_LEVEL - 20)
2310                                 continue;
2311                         n->setContent(c_dirt_with_grass);
2312                 }
2313         }
2314
2315         /*
2316                 Generate some trees
2317         */
2318         assert(central_area_size.X == central_area_size.Z);
2319         {
2320                 // Divide area into parts
2321                 s16 div = 8;
2322                 s16 sidelen = central_area_size.X / div;
2323                 double area = sidelen * sidelen;
2324                 for(s16 x0=0; x0<div; x0++)
2325                 for(s16 z0=0; z0<div; z0++)
2326                 {
2327                         // Center position of part of division
2328                         v2s16 p2d_center(
2329                                 node_min.X + sidelen/2 + sidelen*x0,
2330                                 node_min.Z + sidelen/2 + sidelen*z0
2331                         );
2332                         // Minimum edge of part of division
2333                         v2s16 p2d_min(
2334                                 node_min.X + sidelen*x0,
2335                                 node_min.Z + sidelen*z0
2336                         );
2337                         // Maximum edge of part of division
2338                         v2s16 p2d_max(
2339                                 node_min.X + sidelen + sidelen*x0 - 1,
2340                                 node_min.Z + sidelen + sidelen*z0 - 1
2341                         );
2342                         // Amount of trees
2343                         u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
2344                         // Put trees in random places on part of division
2345                         for(u32 i=0; i<tree_count; i++)
2346                         {
2347                                 s16 x = myrand_range(p2d_min.X, p2d_max.X);
2348                                 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
2349                                 s16 y = find_ground_level(vmanip, v2s16(x,z), ndef);
2350                                 // Don't make a tree under water level
2351                                 if(y < WATER_LEVEL)
2352                                         continue;
2353                                 // Don't make a tree so high that it doesn't fit
2354                                 if(y > node_max.Y - 6)
2355                                         continue;
2356                                 v3s16 p(x,y,z);
2357                                 /*
2358                                         Trees grow only on mud and grass
2359                                 */
2360                                 {
2361                                         u32 i = vmanip.m_area.index(v3s16(p));
2362                                         MapNode *n = &vmanip.m_data[i];
2363                                         if(n->getContent() != c_dirt
2364                                                         && n->getContent() != c_dirt_with_grass)
2365                                                 continue;
2366                                 }
2367                                 p.Y++;
2368                                 // Make a tree
2369                                 treegen::make_tree(vmanip, p, false, ndef);
2370                         }
2371                 }
2372         }
2373
2374 #if 0
2375         /*
2376                 Make base ground level
2377         */
2378
2379         for(s16 x=node_min.X; x<=node_max.X; x++)
2380         for(s16 z=node_min.Z; z<=node_max.Z; z++)
2381         {
2382                 // Node position
2383                 v2s16 p2d(x,z);
2384                 {
2385                         // Use fast index incrementing
2386                         v3s16 em = vmanip.m_area.getExtent();
2387                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
2388                         for(s16 y=node_min.Y; y<=node_max.Y; y++)
2389                         {
2390                                 // Only modify places that have no content
2391                                 if(vmanip.m_data[i].getContent() == CONTENT_IGNORE)
2392                                 {
2393                                         // First priority: make air and water.
2394                                         // This avoids caves inside water.
2395                                         if(all_is_ground_except_caves == false
2396                                                         && val_is_ground(noisebuf_ground.get(x,y,z),
2397                                                         v3s16(x,y,z), data->seed) == false)
2398                                         {
2399                                                 if(y <= WATER_LEVEL)
2400                                                         vmanip.m_data[i] = n_water_source;
2401                                                 else
2402                                                         vmanip.m_data[i] = n_air;
2403                                         }
2404                                         else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
2405                                                 vmanip.m_data[i] = n_air;
2406                                         else
2407                                                 vmanip.m_data[i] = n_stone;
2408                                 }
2409
2410                                 vmanip->m_area.add_y(em, i, 1);
2411                         }
2412                 }
2413         }
2414
2415         /*
2416                 Add mud and sand and others underground (in place of stone)
2417         */
2418
2419         for(s16 x=node_min.X; x<=node_max.X; x++)
2420         for(s16 z=node_min.Z; z<=node_max.Z; z++)
2421         {
2422                 // Node position
2423                 v2s16 p2d(x,z);
2424                 {
2425                         // Use fast index incrementing
2426                         v3s16 em = vmanip.m_area.getExtent();
2427                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
2428                         for(s16 y=node_max.Y; y>=node_min.Y; y--)
2429                         {
2430                                 if(vmanip.m_data[i].getContent() == c_stone)
2431                                 {
2432                                         if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
2433                                         {
2434                                                 if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
2435                                                         vmanip.m_data[i] = n_dirt;
2436                                                 else
2437                                                         vmanip.m_data[i] = n_sand;
2438                                         }
2439                                         else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
2440                                         {
2441                                                 if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
2442                                                         vmanip.m_data[i] = n_gravel;
2443                                         }
2444                                         else if(noisebuf_ground_crumbleness.get(x,y,z) <
2445                                                         -3.0 + MYMIN(0.1 * sqrt((float)MYMAX(0, -y)), 1.5))
2446                                         {
2447                                                 vmanip.m_data[i] = n_lava_source;
2448                                                 for(s16 x1=-1; x1<=1; x1++)
2449                                                 for(s16 y1=-1; y1<=1; y1++)
2450                                                 for(s16 z1=-1; z1<=1; z1++)
2451                                                         data->transforming_liquid.push_back(
2452                                                                         v3s16(p2d.X+x1, y+y1, p2d.Y+z1));
2453                                         }
2454                                 }
2455
2456                                 vmanip->m_area.add_y(em, i, -1);
2457                         }
2458                 }
2459         }
2460
2461         /*
2462                 Add dungeons
2463         */
2464
2465         //if(node_min.Y < approx_groundlevel)
2466         //if(myrand() % 3 == 0)
2467         //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
2468         //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
2469         //float dungeon_rarity = g_settings.getFloat("dungeon_rarity");
2470         float dungeon_rarity = 0.02;
2471         if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0)
2472                         < dungeon_rarity
2473                         && node_min.Y < approx_groundlevel)
2474         {
2475                 // Dungeon generator doesn't modify places which have this set
2476                 vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
2477                                 | VMANIP_FLAG_DUNGEON_PRESERVE);
2478
2479                 // Set all air and water to be untouchable to make dungeons open
2480                 // to caves and open air
2481                 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
2482                 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
2483                 {
2484                         // Node position
2485                         v2s16 p2d(x,z);
2486                         {
2487                                 // Use fast index incrementing
2488                                 v3s16 em = vmanip.m_area.getExtent();
2489                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
2490                                 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
2491                                 {
2492                                         if(vmanip.m_data[i].getContent() == CONTENT_AIR)
2493                                                 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
2494                                         else if(vmanip.m_data[i].getContent() == c_water_source)
2495                                                 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
2496                                         vmanip->m_area.add_y(em, i, -1);
2497                                 }
2498                         }
2499                 }
2500
2501                 PseudoRandom random(blockseed+2);
2502
2503                 // Add it
2504                 make_dungeon1(vmanip, random, ndef);
2505
2506                 // Convert some cobble to mossy cobble
2507                 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
2508                 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
2509                 {
2510                         // Node position
2511                         v2s16 p2d(x,z);
2512                         {
2513                                 // Use fast index incrementing
2514                                 v3s16 em = vmanip.m_area.getExtent();
2515                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
2516                                 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
2517                                 {
2518                                         // (noisebuf not used because it doesn't contain the
2519                                         //  full area)
2520                                         double wetness = noise3d_param(
2521                                                         get_ground_wetness_params(data->seed), x,y,z);
2522                                         double d = noise3d_perlin((float)x/2.5,
2523                                                         (float)y/2.5,(float)z/2.5,
2524                                                         blockseed, 2, 1.4);
2525                                         if(vmanip.m_data[i].getContent() == c_cobble)
2526                                         {
2527                                                 if(d < wetness/3.0)
2528                                                 {
2529                                                         vmanip.m_data[i].setContent(c_mossycobble);
2530                                                 }
2531                                         }
2532                                         /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
2533                                         {
2534                                                 if(wetness > 1.2)
2535                                                         vmanip.m_data[i].setContent(c_dirt);
2536                                         }*/
2537                                         vmanip->m_area.add_y(em, i, -1);
2538                                 }
2539                         }
2540                 }
2541         }
2542
2543         /*
2544                 Add NC
2545         */
2546         {
2547                 PseudoRandom ncrandom(blockseed+9324342);
2548                 if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3)
2549                 {
2550                         make_nc(vmanip, ncrandom, ndef);
2551                 }
2552         }
2553
2554         /*
2555                 Add top and bottom side of water to transforming_liquid queue
2556         */
2557
2558         for(s16 x=node_min.X; x<=node_max.X; x++)
2559         for(s16 z=node_min.Z; z<=node_max.Z; z++)
2560         {
2561                 // Node position
2562                 v2s16 p2d(x,z);
2563                 {
2564                         bool water_found = false;
2565                         // Use fast index incrementing
2566                         v3s16 em = vmanip.m_area.getExtent();
2567                         u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
2568                         for(s16 y=node_max.Y; y>=node_min.Y; y--)
2569                         {
2570                                 if(water_found == false)
2571                                 {
2572                                         if(vmanip.m_data[i].getContent() == c_water_source)
2573                                         {
2574                                                 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2575                                                 data->transforming_liquid.push_back(p);
2576                                                 water_found = true;
2577                                         }
2578                                 }
2579                                 else
2580                                 {
2581                                         // This can be done because water_found can only
2582                                         // turn to true and end up here after going through
2583                                         // a single block.
2584                                         if(vmanip.m_data[i+1].getContent() != c_water_source)
2585                                         {
2586                                                 v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
2587                                                 data->transforming_liquid.push_back(p);
2588                                                 water_found = false;
2589                                         }
2590                                 }
2591
2592                                 vmanip->m_area.add_y(em, i, -1);
2593                         }
2594                 }
2595         }
2596
2597         /*
2598                 If close to ground level
2599         */
2600
2601         //if(abs(approx_ground_depth) < 30)
2602         if(minimum_ground_depth < 5 && maximum_ground_depth > -5)
2603         {
2604                 /*
2605                         Add grass and mud
2606                 */
2607
2608                 for(s16 x=node_min.X; x<=node_max.X; x++)
2609                 for(s16 z=node_min.Z; z<=node_max.Z; z++)
2610                 {
2611                         // Node position
2612                         v2s16 p2d(x,z);
2613                         {
2614                                 bool possibly_have_sand = get_have_beach(data->seed, p2d);
2615                                 bool have_sand = false;
2616                                 u32 current_depth = 0;
2617                                 bool air_detected = false;
2618                                 bool water_detected = false;
2619                                 bool have_clay = false;
2620
2621                                 // Use fast index incrementing
2622                                 s16 start_y = node_max.Y+2;
2623                                 v3s16 em = vmanip.m_area.getExtent();
2624                                 u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
2625                                 for(s16 y=start_y; y>=node_min.Y-3; y--)
2626                                 {
2627                                         if(vmanip.m_data[i].getContent() == c_water_source)
2628                                                 water_detected = true;
2629                                         if(vmanip.m_data[i].getContent() == CONTENT_AIR)
2630                                                 air_detected = true;
2631
2632                                         if((vmanip.m_data[i].getContent() == c_stone
2633                                                         || vmanip.m_data[i].getContent() == c_dirt_with_grass
2634                                                         || vmanip.m_data[i].getContent() == c_dirt
2635                                                         || vmanip.m_data[i].getContent() == c_sand
2636                                                         || vmanip.m_data[i].getContent() == c_gravel
2637                                                         ) && (air_detected || water_detected))
2638                                         {
2639                                                 if(current_depth == 0 && y <= WATER_LEVEL+2
2640                                                                 && possibly_have_sand)
2641                                                         have_sand = true;
2642
2643                                                 if(current_depth < 4)
2644                                                 {
2645                                                         if(have_sand)
2646                                                         {
2647                                                                 vmanip.m_data[i] = MapNode(c_sand);
2648                                                         }
2649                                                         #if 1
2650                                                         else if(current_depth==0 && !water_detected
2651                                                                         && y >= WATER_LEVEL && air_detected)
2652                                                                 vmanip.m_data[i] = MapNode(c_dirt_with_grass);
2653                                                         #endif
2654                                                         else
2655                                                                 vmanip.m_data[i] = MapNode(c_dirt);
2656                                                 }
2657                                                 else
2658                                                 {
2659                                                         if(vmanip.m_data[i].getContent() == c_dirt
2660                                                                 || vmanip.m_data[i].getContent() == c_dirt_with_grass)
2661                                                                 vmanip.m_data[i] = MapNode(c_stone);
2662                                                 }
2663
2664                                                 current_depth++;
2665
2666                                                 if(current_depth >= 8)
2667                                                         break;
2668                                         }
2669                                         else if(current_depth != 0)
2670                                                 break;
2671
2672                                         vmanip->m_area.add_y(em, i, -1);
2673                                 }
2674                         }
2675                 }
2676
2677                 /*
2678                         Calculate some stuff
2679                 */
2680
2681                 float surface_humidity = surface_humidity_2d(data->seed, p2d_center);
2682                 bool is_jungle = surface_humidity > 0.75;
2683                 // Amount of trees
2684                 u32 tree_count = gen_area_nodes * tree_amount_2d(data->seed, p2d_center);
2685                 if(is_jungle)
2686                         tree_count *= 5;
2687
2688                 /*
2689                         Add trees
2690                 */
2691                 PseudoRandom treerandom(blockseed);
2692                 // Put trees in random places on part of division
2693                 for(u32 i=0; i<tree_count; i++)
2694                 {
2695                         s16 x = treerandom.range(node_min.X, node_max.X);
2696                         s16 z = treerandom.range(node_min.Z, node_max.Z);
2697                         //s16 y = find_ground_level(vmanip, v2s16(x,z));
2698                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
2699                         // Don't make a tree under water level
2700                         if(y < WATER_LEVEL)
2701                                 continue;
2702                         // Make sure tree fits (only trees whose starting point is
2703                         // at this block are added)
2704                         if(y < node_min.Y || y > node_max.Y)
2705                                 continue;
2706                         /*
2707                                 Find exact ground level
2708                         */
2709                         v3s16 p(x,y+6,z);
2710                         bool found = false;
2711                         for(; p.Y >= y-6; p.Y--)
2712                         {
2713                                 u32 i = vmanip->m_area.index(p);
2714                                 MapNode *n = &vmanip->m_data[i];
2715                                 if(n->getContent() != CONTENT_AIR && n->getContent() != c_water_source && n->getContent() != CONTENT_IGNORE)
2716                                 {
2717                                         found = true;
2718                                         break;
2719                                 }
2720                         }
2721                         // If not found, handle next one
2722                         if(found == false)
2723                                 continue;
2724
2725                         {
2726                                 u32 i = vmanip->m_area.index(p);
2727                                 MapNode *n = &vmanip->m_data[i];
2728
2729                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass && n->getContent() != c_sand)
2730                                                 continue;
2731
2732                                 // Papyrus grows only on mud and in water
2733                                 if(n->getContent() == c_dirt && y <= WATER_LEVEL)
2734                                 {
2735                                         p.Y++;
2736                                         make_papyrus(vmanip, p, ndef);
2737                                 }
2738                                 // Trees grow only on mud and grass, on land
2739                                 else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2)
2740                                 {
2741                                         p.Y++;
2742                                         //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5)
2743                                         if(is_jungle == false)
2744                                         {
2745                                                 bool is_apple_tree;
2746                                                 if(myrand_range(0,4) != 0)
2747                                                         is_apple_tree = false;
2748                                                 else
2749                                                         is_apple_tree = noise2d_perlin(
2750                                                                         0.5+(float)p.X/100, 0.5+(float)p.Z/100,
2751                                                                         data->seed+342902, 3, 0.45) > 0.2;
2752                                                 make_tree(vmanip, p, is_apple_tree, ndef);
2753                                         }
2754                                         else
2755                                                 make_jungletree(vmanip, p, ndef);
2756                                 }
2757                                 // Cactii grow only on sand, on land
2758                                 else if(n->getContent() == c_sand && y > WATER_LEVEL + 2)
2759                                 {
2760                                         p.Y++;
2761                                         make_cactus(vmanip, p, ndef);
2762                                 }
2763                         }
2764                 }
2765
2766                 /*
2767                         Add jungle grass
2768                 */
2769                 if(is_jungle)
2770                 {
2771                         PseudoRandom grassrandom(blockseed);
2772                         for(u32 i=0; i<surface_humidity*5*tree_count; i++)
2773                         {
2774                                 s16 x = grassrandom.range(node_min.X, node_max.X);
2775                                 s16 z = grassrandom.range(node_min.Z, node_max.Z);
2776                                 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
2777                                 if(y < WATER_LEVEL)
2778                                         continue;
2779                                 if(y < node_min.Y || y > node_max.Y)
2780                                         continue;
2781                                 /*
2782                                         Find exact ground level
2783                                 */
2784                                 v3s16 p(x,y+6,z);
2785                                 bool found = false;
2786                                 for(; p.Y >= y-6; p.Y--)
2787                                 {
2788                                         u32 i = vmanip->m_area.index(p);
2789                                         MapNode *n = &vmanip->m_data[i];
2790                                         if(data->nodedef->get(*n).is_ground_content)
2791                                         {
2792                                                 found = true;
2793                                                 break;
2794                                         }
2795                                 }
2796                                 // If not found, handle next one
2797                                 if(found == false)
2798                                         continue;
2799                                 p.Y++;
2800                                 if(vmanip.m_area.contains(p) == false)
2801                                         continue;
2802                                 if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR)
2803                                         continue;
2804                                 /*p.Y--;
2805                                 if(vmanip.m_area.contains(p))
2806                                         vmanip.m_data[vmanip.m_area.index(p)] = c_dirt;
2807                                 p.Y++;*/
2808                                 if(vmanip.m_area.contains(p))
2809                                         vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass;
2810                         }
2811                 }
2812
2813 #if 0
2814                 /*
2815                         Add some kind of random stones
2816                 */
2817
2818                 u32 random_stone_count = gen_area_nodes *
2819                                 randomstone_amount_2d(data->seed, p2d_center);
2820                 // Put in random places on part of division
2821                 for(u32 i=0; i<random_stone_count; i++)
2822                 {
2823                         s16 x = myrand_range(node_min.X, node_max.X);
2824                         s16 z = myrand_range(node_min.Z, node_max.Z);
2825                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
2826                         // Don't add under water level
2827                         /*if(y < WATER_LEVEL)
2828                                 continue;*/
2829                         // Don't add if doesn't belong to this block
2830                         if(y < node_min.Y || y > node_max.Y)
2831                                 continue;
2832                         v3s16 p(x,y,z);
2833                         // Filter placement
2834                         /*{
2835                                 u32 i = vmanip->m_area.index(v3s16(p));
2836                                 MapNode *n = &vmanip->m_data[i];
2837                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
2838                                         continue;
2839                         }*/
2840                         // Will be placed one higher
2841                         p.Y++;
2842                         // Add it
2843                         make_randomstone(vmanip, p);
2844                 }
2845 #endif
2846
2847 #if 0
2848                 /*
2849                         Add larger stones
2850                 */
2851
2852                 u32 large_stone_count = gen_area_nodes *
2853                                 largestone_amount_2d(data->seed, p2d_center);
2854                 //u32 large_stone_count = 1;
2855                 // Put in random places on part of division
2856                 for(u32 i=0; i<large_stone_count; i++)
2857                 {
2858                         s16 x = myrand_range(node_min.X, node_max.X);
2859                         s16 z = myrand_range(node_min.Z, node_max.Z);
2860                         s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
2861                         // Don't add under water level
2862                         /*if(y < WATER_LEVEL)
2863                                 continue;*/
2864                         // Don't add if doesn't belong to this block
2865                         if(y < node_min.Y || y > node_max.Y)
2866                                 continue;
2867                         v3s16 p(x,y,z);
2868                         // Filter placement
2869                         /*{
2870                                 u32 i = vmanip->m_area.index(v3s16(p));
2871                                 MapNode *n = &vmanip->m_data[i];
2872                                 if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass)
2873                                         continue;
2874                         }*/
2875                         // Will be placed one lower
2876                         p.Y--;
2877                         // Add it
2878                         make_largestone(vmanip, p);
2879                 }
2880 #endif
2881         }
2882
2883         /*
2884                 Add minerals
2885         */
2886
2887         {
2888                 PseudoRandom mineralrandom(blockseed);
2889
2890                 /*
2891                         Add meseblocks
2892                 */
2893                 for(s16 i=0; i<approx_ground_depth/4; i++)
2894                 {
2895                         if(mineralrandom.next()%50 == 0)
2896                         {
2897                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2898                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2899                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2900                                 for(u16 i=0; i<27; i++)
2901                                 {
2902                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2903                                         u32 vi = vmanip.m_area.index(p);
2904                                         if(vmanip.m_data[vi].getContent() == c_stone)
2905                                                 if(mineralrandom.next()%8 == 0)
2906                                                         vmanip.m_data[vi] = MapNode(c_mese);
2907                                 }
2908
2909                         }
2910                 }
2911                 /*
2912                         Add others
2913                 */
2914                 {
2915                         u16 a = mineralrandom.range(0,15);
2916                         a = a*a*a;
2917                         u16 amount = 20 * a/1000;
2918                         for(s16 i=0; i<amount; i++)
2919                         {
2920                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2921                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2922                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2923
2924                                 u8 base_content = c_stone;
2925                                 MapNode new_content(CONTENT_IGNORE);
2926                                 u32 sparseness = 6;
2927
2928                                 if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
2929                                 {
2930                                         new_content = MapNode(c_stone_with_coal);
2931                                 }
2932                                 else
2933                                 {
2934                                         if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
2935                                                 new_content = MapNode(c_stone_with_iron);
2936                                         /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
2937                                                 vmanip.m_data[i] = MapNode(c_dirt);
2938                                         else
2939                                                 vmanip.m_data[i] = MapNode(c_sand);*/
2940                                 }
2941                                 /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
2942                                 {
2943                                 }*/
2944
2945                                 if(new_content.getContent() != CONTENT_IGNORE)
2946                                 {
2947                                         for(u16 i=0; i<27; i++)
2948                                         {
2949                                                 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2950                                                 u32 vi = vmanip.m_area.index(p);
2951                                                 if(vmanip.m_data[vi].getContent() == base_content)
2952                                                 {
2953                                                         if(mineralrandom.next()%sparseness == 0)
2954                                                                 vmanip.m_data[vi] = new_content;
2955                                                 }
2956                                         }
2957                                 }
2958                         }
2959                 }
2960                 /*
2961                         Add coal
2962                 */
2963                 //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
2964                 //for(s16 i=0; i<50; i++)
2965                 u16 coal_amount = 30;
2966                 u16 coal_rareness = 60 / coal_amount;
2967                 if(coal_rareness == 0)
2968                         coal_rareness = 1;
2969                 if(mineralrandom.next()%coal_rareness == 0)
2970                 {
2971                         u16 a = mineralrandom.next() % 16;
2972                         u16 amount = coal_amount * a*a*a / 1000;
2973                         for(s16 i=0; i<amount; i++)
2974                         {
2975                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
2976                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
2977                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
2978                                 for(u16 i=0; i<27; i++)
2979                                 {
2980                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
2981                                         u32 vi = vmanip.m_area.index(p);
2982                                         if(vmanip.m_data[vi].getContent() == c_stone)
2983                                                 if(mineralrandom.next()%8 == 0)
2984                                                         vmanip.m_data[vi] = MapNode(c_stone_with_coal);
2985                                 }
2986                         }
2987                 }
2988                 /*
2989                         Add iron
2990                 */
2991                 u16 iron_amount = 8;
2992                 u16 iron_rareness = 60 / iron_amount;
2993                 if(iron_rareness == 0)
2994                         iron_rareness = 1;
2995                 if(mineralrandom.next()%iron_rareness == 0)
2996                 {
2997                         u16 a = mineralrandom.next() % 16;
2998                         u16 amount = iron_amount * a*a*a / 1000;
2999                         for(s16 i=0; i<amount; i++)
3000                         {
3001                                 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
3002                                 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
3003                                 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
3004                                 for(u16 i=0; i<27; i++)
3005                                 {
3006                                         v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3007                                         u32 vi = vmanip.m_area.index(p);
3008                                         if(vmanip.m_data[vi].getContent() == c_stone)
3009                                                 if(mineralrandom.next()%8 == 0)
3010                                                         vmanip.m_data[vi] = MapNode(c_stone_with_iron);
3011                                 }
3012                         }
3013                 }
3014         }
3015 #endif
3016
3017         /*
3018                 Calculate lighting
3019         */
3020         {
3021         ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update",
3022                         SPT_AVG);
3023         //VoxelArea a(node_min, node_max);
3024         VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE,
3025                         node_max+v3s16(1,0,1)*MAP_BLOCKSIZE);
3026         /*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2,
3027                         node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/
3028         enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
3029         for(int i=0; i<2; i++)
3030         {
3031                 enum LightBank bank = banks[i];
3032
3033                 core::map<v3s16, bool> light_sources;
3034                 core::map<v3s16, u8> unlight_from;
3035
3036                 voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef,
3037                                 light_sources, unlight_from);
3038
3039                 bool inexistent_top_provides_sunlight = !block_is_underground;
3040                 voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
3041                                 vmanip, a, inexistent_top_provides_sunlight,
3042                                 light_sources, ndef);
3043                 // TODO: Do stuff according to bottom_sunlight_valid
3044
3045                 vmanip.unspreadLight(bank, unlight_from, light_sources, ndef);
3046
3047                 vmanip.spreadLight(bank, light_sources, ndef);
3048         }
3049         }
3050 }
3051
3052 #endif ///BIG COMMENT
3053
3054 BlockMakeData::BlockMakeData():
3055         no_op(false),
3056         vmanip(NULL),
3057         seed(0),
3058         nodedef(NULL)
3059 {}
3060
3061 BlockMakeData::~BlockMakeData()
3062 {
3063         delete vmanip;
3064 }
3065
3066 //}; // namespace mapgen
3067
3068