]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapgen/mapgen_v6.cpp
Settings: Add get_flags API for mapgen flags (mg_flags, mgv6_spflags, ...) (#9284)
[dragonfireclient.git] / src / mapgen / mapgen_v6.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 Copyright (C) 2014-2018 paramat
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22
23 #include <cmath>
24 #include "mapgen.h"
25 #include "voxel.h"
26 #include "noise.h"
27 #include "mapblock.h"
28 #include "mapnode.h"
29 #include "map.h"
30 //#include "serverobject.h"
31 #include "content_sao.h"
32 #include "nodedef.h"
33 #include "voxelalgorithms.h"
34 //#include "profiler.h" // For TimeTaker
35 #include "settings.h" // For g_settings
36 #include "emerge.h"
37 #include "dungeongen.h"
38 #include "cavegen.h"
39 #include "treegen.h"
40 #include "mg_ore.h"
41 #include "mg_decoration.h"
42 #include "mapgen_v6.h"
43
44
45 FlagDesc flagdesc_mapgen_v6[] = {
46         {"jungles",    MGV6_JUNGLES},
47         {"biomeblend", MGV6_BIOMEBLEND},
48         {"mudflow",    MGV6_MUDFLOW},
49         {"snowbiomes", MGV6_SNOWBIOMES},
50         {"flat",       MGV6_FLAT},
51         {"trees",      MGV6_TREES},
52         {NULL,         0}
53 };
54
55
56 /////////////////////////////////////////////////////////////////////////////
57
58
59 MapgenV6::MapgenV6(MapgenV6Params *params, EmergeManager *emerge)
60         : Mapgen(MAPGEN_V6, params, emerge)
61 {
62         m_emerge = emerge;
63         ystride = csize.X;
64
65         heightmap = new s16[csize.X * csize.Z];
66
67         spflags      = params->spflags;
68         freq_desert  = params->freq_desert;
69         freq_beach   = params->freq_beach;
70         dungeon_ymin = params->dungeon_ymin;
71         dungeon_ymax = params->dungeon_ymax;
72
73         np_cave        = &params->np_cave;
74         np_humidity    = &params->np_humidity;
75         np_trees       = &params->np_trees;
76         np_apple_trees = &params->np_apple_trees;
77
78         np_dungeons = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
79
80         //// Create noise objects
81         noise_terrain_base   = new Noise(&params->np_terrain_base,   seed, csize.X, csize.Y);
82         noise_terrain_higher = new Noise(&params->np_terrain_higher, seed, csize.X, csize.Y);
83         noise_steepness      = new Noise(&params->np_steepness,      seed, csize.X, csize.Y);
84         noise_height_select  = new Noise(&params->np_height_select,  seed, csize.X, csize.Y);
85         noise_mud            = new Noise(&params->np_mud,            seed, csize.X, csize.Y);
86         noise_beach          = new Noise(&params->np_beach,          seed, csize.X, csize.Y);
87         noise_biome          = new Noise(&params->np_biome,          seed,
88                         csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
89         noise_humidity       = new Noise(&params->np_humidity,       seed,
90                         csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
91
92         //// Resolve nodes to be used
93         const NodeDefManager *ndef = emerge->ndef;
94
95         c_stone           = ndef->getId("mapgen_stone");
96         c_dirt            = ndef->getId("mapgen_dirt");
97         c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
98         c_sand            = ndef->getId("mapgen_sand");
99         c_water_source    = ndef->getId("mapgen_water_source");
100         c_lava_source     = ndef->getId("mapgen_lava_source");
101         c_gravel          = ndef->getId("mapgen_gravel");
102         c_desert_stone    = ndef->getId("mapgen_desert_stone");
103         c_desert_sand     = ndef->getId("mapgen_desert_sand");
104         c_dirt_with_snow  = ndef->getId("mapgen_dirt_with_snow");
105         c_snow            = ndef->getId("mapgen_snow");
106         c_snowblock       = ndef->getId("mapgen_snowblock");
107         c_ice             = ndef->getId("mapgen_ice");
108
109         if (c_gravel == CONTENT_IGNORE)
110                 c_gravel = c_stone;
111         if (c_desert_stone == CONTENT_IGNORE)
112                 c_desert_stone = c_stone;
113         if (c_desert_sand == CONTENT_IGNORE)
114                 c_desert_sand = c_sand;
115         if (c_dirt_with_snow == CONTENT_IGNORE)
116                 c_dirt_with_snow = c_dirt_with_grass;
117         if (c_snow == CONTENT_IGNORE)
118                 c_snow = CONTENT_AIR;
119         if (c_snowblock == CONTENT_IGNORE)
120                 c_snowblock = c_dirt_with_grass;
121         if (c_ice == CONTENT_IGNORE)
122                 c_ice = c_water_source;
123
124         c_cobble             = ndef->getId("mapgen_cobble");
125         c_mossycobble        = ndef->getId("mapgen_mossycobble");
126         c_stair_cobble       = ndef->getId("mapgen_stair_cobble");
127         c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
128
129         if (c_mossycobble == CONTENT_IGNORE)
130                 c_mossycobble = c_cobble;
131         if (c_stair_cobble == CONTENT_IGNORE)
132                 c_stair_cobble = c_cobble;
133         if (c_stair_desert_stone == CONTENT_IGNORE)
134                 c_stair_desert_stone = c_desert_stone;
135 }
136
137
138 MapgenV6::~MapgenV6()
139 {
140         delete noise_terrain_base;
141         delete noise_terrain_higher;
142         delete noise_steepness;
143         delete noise_height_select;
144         delete noise_mud;
145         delete noise_beach;
146         delete noise_biome;
147         delete noise_humidity;
148
149         delete[] heightmap;
150 }
151
152
153 MapgenV6Params::MapgenV6Params():
154         np_terrain_base   (-4,   20.0, v3f(250.0, 250.0, 250.0), 82341,  5, 0.6,  2.0),
155         np_terrain_higher (20,   16.0, v3f(500.0, 500.0, 500.0), 85039,  5, 0.6,  2.0),
156         np_steepness      (0.85, 0.5,  v3f(125.0, 125.0, 125.0), -932,   5, 0.7,  2.0),
157         np_height_select  (0,    1.0,  v3f(250.0, 250.0, 250.0), 4213,   5, 0.69, 2.0),
158         np_mud            (4,    2.0,  v3f(200.0, 200.0, 200.0), 91013,  3, 0.55, 2.0),
159         np_beach          (0,    1.0,  v3f(250.0, 250.0, 250.0), 59420,  3, 0.50, 2.0),
160         np_biome          (0,    1.0,  v3f(500.0, 500.0, 500.0), 9130,   3, 0.50, 2.0),
161         np_cave           (6,    6.0,  v3f(250.0, 250.0, 250.0), 34329,  3, 0.50, 2.0),
162         np_humidity       (0.5,  0.5,  v3f(500.0, 500.0, 500.0), 72384,  3, 0.50, 2.0),
163         np_trees          (0,    1.0,  v3f(125.0, 125.0, 125.0), 2,      4, 0.66, 2.0),
164         np_apple_trees    (0,    1.0,  v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
165 {
166 }
167
168
169 void MapgenV6Params::readParams(const Settings *settings)
170 {
171         settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
172         settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
173         settings->getFloatNoEx("mgv6_freq_beach",  freq_beach);
174         settings->getS16NoEx("mgv6_dungeon_ymin",  dungeon_ymin);
175         settings->getS16NoEx("mgv6_dungeon_ymax",  dungeon_ymax);
176
177         settings->getNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
178         settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
179         settings->getNoiseParams("mgv6_np_steepness",      np_steepness);
180         settings->getNoiseParams("mgv6_np_height_select",  np_height_select);
181         settings->getNoiseParams("mgv6_np_mud",            np_mud);
182         settings->getNoiseParams("mgv6_np_beach",          np_beach);
183         settings->getNoiseParams("mgv6_np_biome",          np_biome);
184         settings->getNoiseParams("mgv6_np_cave",           np_cave);
185         settings->getNoiseParams("mgv6_np_humidity",       np_humidity);
186         settings->getNoiseParams("mgv6_np_trees",          np_trees);
187         settings->getNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
188 }
189
190
191 void MapgenV6Params::writeParams(Settings *settings) const
192 {
193         settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6);
194         settings->setFloat("mgv6_freq_desert", freq_desert);
195         settings->setFloat("mgv6_freq_beach",  freq_beach);
196         settings->setS16("mgv6_dungeon_ymin",  dungeon_ymin);
197         settings->setS16("mgv6_dungeon_ymax",  dungeon_ymax);
198
199         settings->setNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
200         settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
201         settings->setNoiseParams("mgv6_np_steepness",      np_steepness);
202         settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
203         settings->setNoiseParams("mgv6_np_mud",            np_mud);
204         settings->setNoiseParams("mgv6_np_beach",          np_beach);
205         settings->setNoiseParams("mgv6_np_biome",          np_biome);
206         settings->setNoiseParams("mgv6_np_cave",           np_cave);
207         settings->setNoiseParams("mgv6_np_humidity",       np_humidity);
208         settings->setNoiseParams("mgv6_np_trees",          np_trees);
209         settings->setNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
210 }
211
212
213 void MapgenV6Params::setDefaultSettings(Settings *settings)
214 {
215         settings->setDefault("mgv6_spflags", flagdesc_mapgen_v6, MGV6_JUNGLES |
216                 MGV6_SNOWBIOMES | MGV6_TREES | MGV6_BIOMEBLEND | MGV6_MUDFLOW);
217 }
218
219
220 //////////////////////// Some helper functions for the map generator
221
222
223 // Returns Y one under area minimum if not found
224 s16 MapgenV6::find_stone_level(v2s16 p2d)
225 {
226         const v3s16 &em = vm->m_area.getExtent();
227         s16 y_nodes_max = vm->m_area.MaxEdge.Y;
228         s16 y_nodes_min = vm->m_area.MinEdge.Y;
229         u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
230         s16 y;
231
232         for (y = y_nodes_max; y >= y_nodes_min; y--) {
233                 content_t c = vm->m_data[i].getContent();
234                 if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
235                         break;
236
237                 VoxelArea::add_y(em, i, -1);
238         }
239         return (y >= y_nodes_min) ? y : y_nodes_min - 1;
240 }
241
242
243 // Required by mapgen.h
244 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
245 {
246         /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
247                         seed, v2s16(blockpos.X, blockpos.Z));*/
248         // Nah, this is just a heuristic, just return something
249         s16 minimum_groundlevel = water_level;
250
251         if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
252                 return true;
253
254         return false;
255 }
256
257
258 //////////////////////// Base terrain height functions
259
260 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
261         float steepness, float height_select)
262 {
263         float base   = 1 + terrain_base;
264         float higher = 1 + terrain_higher;
265
266         // Limit higher ground level to at least base
267         if(higher < base)
268                 higher = base;
269
270         // Steepness factor of cliffs
271         float b = steepness;
272         b = rangelim(b, 0.0, 1000.0);
273         b = 5 * b * b * b * b * b * b * b;
274         b = rangelim(b, 0.5, 1000.0);
275
276         // Values 1.5...100 give quite horrible looking slopes
277         if (b > 1.5 && b < 100.0)
278                 b = (b < 10.0) ? 1.5 : 100.0;
279
280         float a_off = -0.20; // Offset to more low
281         float a = 0.5 + b * (a_off + height_select);
282         a = rangelim(a, 0.0, 1.0); // Limit
283
284         return base * (1.0 - a) + higher * a;
285 }
286
287
288 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
289 {
290         if (spflags & MGV6_FLAT)
291                 return water_level;
292
293         float terrain_base   = NoisePerlin2D_PO(&noise_terrain_base->np,
294                                                         p.X, 0.5, p.Y, 0.5, seed);
295         float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
296                                                         p.X, 0.5, p.Y, 0.5, seed);
297         float steepness      = NoisePerlin2D_PO(&noise_steepness->np,
298                                                         p.X, 0.5, p.Y, 0.5, seed);
299         float height_select  = NoisePerlin2D_PO(&noise_height_select->np,
300                                                         p.X, 0.5, p.Y, 0.5, seed);
301
302         return baseTerrainLevel(terrain_base, terrain_higher,
303                                                         steepness, height_select);
304 }
305
306
307 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
308 {
309         int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
310         return baseTerrainLevelFromMap(index);
311 }
312
313
314 float MapgenV6::baseTerrainLevelFromMap(int index)
315 {
316         if (spflags & MGV6_FLAT)
317                 return water_level;
318
319         float terrain_base   = noise_terrain_base->result[index];
320         float terrain_higher = noise_terrain_higher->result[index];
321         float steepness      = noise_steepness->result[index];
322         float height_select  = noise_height_select->result[index];
323
324         return baseTerrainLevel(terrain_base, terrain_higher,
325                                                         steepness, height_select);
326 }
327
328
329 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
330 {
331         return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
332 }
333
334
335 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
336 {
337         return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
338 }
339
340
341 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
342 {
343         s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
344         if (level_at_point <= water_level ||
345                         level_at_point > water_level + 16)
346                 return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
347
348         return level_at_point;
349 }
350
351
352 //////////////////////// Noise functions
353
354 float MapgenV6::getMudAmount(v2s16 p)
355 {
356         int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
357         return getMudAmount(index);
358 }
359
360
361 bool MapgenV6::getHaveBeach(v2s16 p)
362 {
363         int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
364         return getHaveBeach(index);
365 }
366
367
368 BiomeV6Type MapgenV6::getBiome(v2s16 p)
369 {
370         int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
371                         + (p.X - full_node_min.X);
372         return getBiome(index, p);
373 }
374
375
376 float MapgenV6::getHumidity(v2s16 p)
377 {
378         /*double noise = noise2d_perlin(
379                 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
380                 seed+72384, 4, 0.66);
381         noise = (noise + 1.0)/2.0;*/
382
383         int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
384                         + (p.X - full_node_min.X);
385         float noise = noise_humidity->result[index];
386
387         if (noise < 0.0)
388                 noise = 0.0;
389         if (noise > 1.0)
390                 noise = 1.0;
391         return noise;
392 }
393
394
395 float MapgenV6::getTreeAmount(v2s16 p)
396 {
397         /*double noise = noise2d_perlin(
398                         0.5+(float)p.X/125, 0.5+(float)p.Y/125,
399                         seed+2, 4, 0.66);*/
400
401         float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
402         float zeroval = -0.39;
403         if (noise < zeroval)
404                 return 0;
405
406         return 0.04 * (noise - zeroval) / (1.0 - zeroval);
407 }
408
409
410 bool MapgenV6::getHaveAppleTree(v2s16 p)
411 {
412         /*is_apple_tree = noise2d_perlin(
413                 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
414                 data->seed+342902, 3, 0.45) > 0.2;*/
415
416         float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
417
418         return noise > 0.2;
419 }
420
421
422 float MapgenV6::getMudAmount(int index)
423 {
424         if (spflags & MGV6_FLAT)
425                 return MGV6_AVERAGE_MUD_AMOUNT;
426
427         /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
428                         0.5+(float)p.X/200, 0.5+(float)p.Y/200,
429                         seed+91013, 3, 0.55));*/
430
431         return noise_mud->result[index];
432 }
433
434
435 bool MapgenV6::getHaveBeach(int index)
436 {
437         // Determine whether to have sand here
438         /*double sandnoise = noise2d_perlin(
439                         0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
440                         seed+59420, 3, 0.50);*/
441
442         float sandnoise = noise_beach->result[index];
443         return (sandnoise > freq_beach);
444 }
445
446
447 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
448 {
449         // Just do something very simple as for now
450         /*double d = noise2d_perlin(
451                         0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
452                         seed+9130, 3, 0.50);*/
453
454         float d = noise_biome->result[index];
455         float h = noise_humidity->result[index];
456
457         if (spflags & MGV6_SNOWBIOMES) {
458                 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
459
460                 if (d > MGV6_FREQ_HOT + blend) {
461                         if (h > MGV6_FREQ_JUNGLE + blend)
462                                 return BT_JUNGLE;
463
464                         return BT_DESERT;
465                 }
466
467                 if (d < MGV6_FREQ_SNOW + blend) {
468                         if (h > MGV6_FREQ_TAIGA + blend)
469                                 return BT_TAIGA;
470
471                         return BT_TUNDRA;
472                 }
473
474                 return BT_NORMAL;
475         }
476
477         if (d > freq_desert)
478                 return BT_DESERT;
479
480         if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
481                         ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
482                 return BT_DESERT;
483
484         if ((spflags & MGV6_JUNGLES) && h > 0.75)
485                 return BT_JUNGLE;
486
487         return BT_NORMAL;
488
489 }
490
491
492 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
493 {
494         s32 x = p.X, y = p.Y, z = p.Z;
495         return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
496 }
497
498
499 //////////////////////// Map generator
500
501 void MapgenV6::makeChunk(BlockMakeData *data)
502 {
503         // Pre-conditions
504         assert(data->vmanip);
505         assert(data->nodedef);
506         assert(data->blockpos_requested.X >= data->blockpos_min.X &&
507                 data->blockpos_requested.Y >= data->blockpos_min.Y &&
508                 data->blockpos_requested.Z >= data->blockpos_min.Z);
509         assert(data->blockpos_requested.X <= data->blockpos_max.X &&
510                 data->blockpos_requested.Y <= data->blockpos_max.Y &&
511                 data->blockpos_requested.Z <= data->blockpos_max.Z);
512
513         this->generating = true;
514         this->vm   = data->vmanip;
515         this->ndef = data->nodedef;
516
517         // Hack: use minimum block coords for old code that assumes a single block
518         v3s16 blockpos_min = data->blockpos_min;
519         v3s16 blockpos_max = data->blockpos_max;
520
521         // Area of central chunk
522         node_min = blockpos_min * MAP_BLOCKSIZE;
523         node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
524
525         // Full allocated area
526         full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
527         full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
528
529         central_area_size = node_max - node_min + v3s16(1, 1, 1);
530         assert(central_area_size.X == central_area_size.Z);
531
532         // Create a block-specific seed
533         blockseed = get_blockseed(data->seed, full_node_min);
534
535         // Make some noise
536         calculateNoise();
537
538         // Maximum height of the stone surface and obstacles.
539         // This is used to guide the cave generation
540         s16 stone_surface_max_y;
541
542         // Generate general ground level to full area
543         stone_surface_max_y = generateGround();
544
545         // Create initial heightmap to limit caves
546         updateHeightmap(node_min, node_max);
547
548         const s16 max_spread_amount = MAP_BLOCKSIZE;
549         // Limit dirt flow area by 1 because mud is flowed into neighbors
550         s16 mudflow_minpos = -max_spread_amount + 1;
551         s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
552
553         // Loop this part, it will make stuff look older and newer nicely
554         const u32 age_loops = 2;
555         for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
556                 // Make caves (this code is relatively horrible)
557                 if (flags & MG_CAVES)
558                         generateCaves(stone_surface_max_y);
559
560                 // Add mud to the central chunk
561                 addMud();
562
563                 // Flow mud away from steep edges
564                 if (spflags & MGV6_MUDFLOW)
565                         flowMud(mudflow_minpos, mudflow_maxpos);
566
567         }
568
569         // Update heightmap after mudflow
570         updateHeightmap(node_min, node_max);
571
572         // Add dungeons
573         if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
574                         full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
575                 u16 num_dungeons = std::fmax(std::floor(
576                         NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
577
578                 if (num_dungeons >= 1) {
579                         PseudoRandom ps(blockseed + 4713);
580
581                         DungeonParams dp;
582
583                         dp.seed              = seed;
584                         dp.num_dungeons      = num_dungeons;
585                         dp.only_in_ground    = true;
586                         dp.corridor_len_min  = 1;
587                         dp.corridor_len_max  = 13;
588                         dp.num_rooms         = ps.range(2, 16);
589                         dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
590
591                         dp.np_alt_wall
592                                 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
593
594                         if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
595                                 dp.c_wall              = c_desert_stone;
596                                 dp.c_alt_wall          = CONTENT_IGNORE;
597                                 dp.c_stair             = c_stair_desert_stone;
598
599                                 dp.diagonal_dirs       = true;
600                                 dp.holesize            = v3s16(2, 3, 2);
601                                 dp.room_size_min       = v3s16(6, 9, 6);
602                                 dp.room_size_max       = v3s16(10, 11, 10);
603                                 dp.room_size_large_min = v3s16(10, 13, 10);
604                                 dp.room_size_large_max = v3s16(18, 21, 18);
605                                 dp.notifytype          = GENNOTIFY_TEMPLE;
606                         } else {
607                                 dp.c_wall              = c_cobble;
608                                 dp.c_alt_wall          = c_mossycobble;
609                                 dp.c_stair             = c_stair_cobble;
610
611                                 dp.diagonal_dirs       = false;
612                                 dp.holesize            = v3s16(1, 2, 1);
613                                 dp.room_size_min       = v3s16(4, 4, 4);
614                                 dp.room_size_max       = v3s16(8, 6, 8);
615                                 dp.room_size_large_min = v3s16(8, 8, 8);
616                                 dp.room_size_large_max = v3s16(16, 16, 16);
617                                 dp.notifytype          = GENNOTIFY_DUNGEON;
618                         }
619
620                         DungeonGen dgen(ndef, &gennotify, &dp);
621                         dgen.generate(vm, blockseed, full_node_min, full_node_max);
622                 }
623         }
624
625         // Add top and bottom side of water to transforming_liquid queue
626         updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
627
628         // Add surface nodes
629         growGrass();
630
631         // Generate some trees, and add grass, if a jungle
632         if (spflags & MGV6_TREES)
633                 placeTreesAndJungleGrass();
634
635         // Generate the registered decorations
636         if (flags & MG_DECORATIONS)
637                 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
638
639         // Generate the registered ores
640         m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
641
642         // Calculate lighting
643         if (flags & MG_LIGHT)
644                 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
645                         node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
646                         full_node_min, full_node_max);
647
648         this->generating = false;
649 }
650
651
652 void MapgenV6::calculateNoise()
653 {
654         int x = node_min.X;
655         int z = node_min.Z;
656         int fx = full_node_min.X;
657         int fz = full_node_min.Z;
658
659         if (!(spflags & MGV6_FLAT)) {
660                 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
661                 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
662                 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
663                 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
664                 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
665         }
666
667         noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
668
669         noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
670         noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
671         // Humidity map does not need range limiting 0 to 1,
672         // only humidity at point does
673 }
674
675
676 int MapgenV6::generateGround()
677 {
678         //TimeTaker timer1("Generating ground level");
679         MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
680         MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
681         MapNode n_ice(c_ice);
682         int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
683
684         u32 index = 0;
685         for (s16 z = node_min.Z; z <= node_max.Z; z++)
686         for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
687                 // Surface height
688                 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
689
690                 // Log it
691                 if (surface_y > stone_surface_max_y)
692                         stone_surface_max_y = surface_y;
693
694                 BiomeV6Type bt = getBiome(v2s16(x, z));
695
696                 // Fill ground with stone
697                 const v3s16 &em = vm->m_area.getExtent();
698                 u32 i = vm->m_area.index(x, node_min.Y, z);
699                 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
700                         if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
701                                 if (y <= surface_y) {
702                                         vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
703                                                         && bt == BT_DESERT) ?
704                                                 n_desert_stone : n_stone;
705                                 } else if (y <= water_level) {
706                                         vm->m_data[i] = (y >= MGV6_ICE_BASE
707                                                         && bt == BT_TUNDRA) ?
708                                                 n_ice : n_water_source;
709                                 } else {
710                                         vm->m_data[i] = n_air;
711                                 }
712                         }
713                         VoxelArea::add_y(em, i, 1);
714                 }
715         }
716
717         return stone_surface_max_y;
718 }
719
720
721 void MapgenV6::addMud()
722 {
723         // 15ms @cs=8
724         //TimeTaker timer1("add mud");
725         MapNode n_dirt(c_dirt), n_gravel(c_gravel);
726         MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
727         MapNode addnode;
728
729         u32 index = 0;
730         for (s16 z = node_min.Z; z <= node_max.Z; z++)
731         for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
732                 // Randomize mud amount
733                 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
734
735                 // Find ground level
736                 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
737
738                 // Handle area not found
739                 if (surface_y == vm->m_area.MinEdge.Y - 1)
740                         continue;
741
742                 BiomeV6Type bt = getBiome(v2s16(x, z));
743                 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
744
745                 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
746                         addnode = n_sand;
747                 } else if (mud_add_amount <= 0) {
748                         mud_add_amount = 1 - mud_add_amount;
749                         addnode = n_gravel;
750                 } else if (bt != BT_DESERT && getHaveBeach(index) &&
751                                 surface_y + mud_add_amount <= water_level + 2) {
752                         addnode = n_sand;
753                 }
754
755                 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
756                         mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
757
758                 /* If topmost node is grass, change it to mud.  It might be if it was
759                 // flown to there from a neighboring chunk and then converted.
760                 u32 i = vm->m_area.index(x, surface_y, z);
761                 if (vm->m_data[i].getContent() == c_dirt_with_grass)
762                         vm->m_data[i] = n_dirt;*/
763
764                 // Add mud on ground
765                 s16 mudcount = 0;
766                 const v3s16 &em = vm->m_area.getExtent();
767                 s16 y_start = surface_y + 1;
768                 u32 i = vm->m_area.index(x, y_start, z);
769                 for (s16 y = y_start; y <= node_max.Y; y++) {
770                         if (mudcount >= mud_add_amount)
771                                 break;
772
773                         vm->m_data[i] = addnode;
774                         mudcount++;
775
776                         VoxelArea::add_y(em, i, 1);
777                 }
778         }
779 }
780
781
782 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
783 {
784         const v3s16 &em = vm->m_area.getExtent();
785         static const v3s16 dirs4[4] = {
786                 v3s16(0, 0, 1), // Back
787                 v3s16(1, 0, 0), // Right
788                 v3s16(0, 0, -1), // Front
789                 v3s16(-1, 0, 0), // Left
790         };
791         
792         // Iterate twice
793         for (s16 k = 0; k < 2; k++) {
794                 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
795                 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
796                         // Node column position
797                         v2s16 p2d;
798                         // Invert coordinates on second iteration to process columns in
799                         // opposite order, to avoid a directional bias.
800                         if (k == 1)
801                                 p2d = v2s16(node_max.X, node_max.Z) - v2s16(x, z);
802                         else
803                                 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
804
805                         s16 y = node_max.Y;
806
807                         while (y >= node_min.Y) {
808                                 for (;; y--) {
809                                         u32 i = vm->m_area.index(p2d.X, y, p2d.Y);
810                                         MapNode *n = nullptr;
811
812                                         // Find next mud node in mapchunk column
813                                         for (; y >= node_min.Y; y--) {
814                                                 n = &vm->m_data[i];
815                                                 if (n->getContent() == c_dirt ||
816                                                                 n->getContent() == c_dirt_with_grass ||
817                                                                 n->getContent() == c_gravel)
818                                                         break;
819
820                                                 VoxelArea::add_y(em, i, -1);
821                                         }
822                                         if (y < node_min.Y)
823                                                 // No mud found in mapchunk column, process the next column
824                                                 break;
825
826                                         if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) {
827                                                 // Convert dirt_with_grass to dirt
828                                                 n->setContent(c_dirt);
829                                                 // Don't flow mud if the stuff under it is not mud,
830                                                 // to leave at least 1 node of mud.
831                                                 u32 i2 = i;
832                                                 VoxelArea::add_y(em, i2, -1);
833                                                 MapNode *n2 = &vm->m_data[i2];
834                                                 if (n2->getContent() != c_dirt &&
835                                                                 n2->getContent() != c_dirt_with_grass)
836                                                         // Find next mud node in column
837                                                         continue;
838                                         }
839
840                                         // Check if node above is walkable. If so, cancel
841                                         // flowing as if node above keeps it in place.
842                                         u32 i3 = i;
843                                         VoxelArea::add_y(em, i3, 1);
844                                         MapNode *n3 = &vm->m_data[i3];
845                                         if (ndef->get(*n3).walkable)
846                                                 // Find next mud node in column
847                                                 continue;
848
849                                         // Drop mud on one side
850                                         for (const v3s16 &dirp : dirs4) {
851                                                 u32 i2 = i;
852                                                 // Move to side
853                                                 VoxelArea::add_p(em, i2, dirp);
854                                                 // Check that side is air
855                                                 MapNode *n2 = &vm->m_data[i2];
856                                                 if (ndef->get(*n2).walkable)
857                                                         continue;
858
859                                                 // Check that under side is air
860                                                 VoxelArea::add_y(em, i2, -1);
861                                                 n2 = &vm->m_data[i2];
862                                                 if (ndef->get(*n2).walkable)
863                                                         continue;
864
865                                                 // Loop further down until not air
866                                                 s16 y2 = y - 1; // y of i2
867                                                 bool dropped_to_unknown = false;
868                                                 do {
869                                                         y2--;
870                                                         VoxelArea::add_y(em, i2, -1);
871                                                         n2 = &vm->m_data[i2];
872                                                         // If out of area or in ungenerated world
873                                                         if (y2 < full_node_min.Y || n2->getContent() == CONTENT_IGNORE) {
874                                                                 dropped_to_unknown = true;
875                                                                 break;
876                                                         }
877                                                 } while (!ndef->get(*n2).walkable);
878
879                                                 if (!dropped_to_unknown) {
880                                                         // Move up one so that we're in air
881                                                         VoxelArea::add_y(em, i2, 1);
882                                                         // Move mud to new place, and if outside mapchunk remove
883                                                         // any decorations above removed or placed mud.
884                                                         moveMud(i, i2, i3, p2d, em);
885                                                 }
886                                                 // Done, find next mud node in column
887                                                 break;
888                                         }
889                                 }
890                         }
891                 }
892         }
893 }
894
895
896 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
897         u32 above_remove_index, v2s16 pos, v3s16 em)
898 {
899         MapNode n_air(CONTENT_AIR);
900         // Copy mud from old place to new place
901         vm->m_data[place_index] = vm->m_data[remove_index];
902         // Set old place to be air
903         vm->m_data[remove_index] = n_air;
904         // Outside the mapchunk decorations may need to be removed if above removed
905         // mud or if half-buried in placed mud. Placed mud is to the side of pos so
906         // use 'pos.X >= node_max.X' etc.
907         if (pos.X >= node_max.X || pos.X <= node_min.X ||
908                         pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
909                 // 'above remove' node is above removed mud. If it is not air, water or
910                 // 'ignore' it is a decoration that needs removing. Also search upwards
911                 // to remove a possible stacked decoration.
912                 // Check for 'ignore' because stacked decorations can penetrate into
913                 // 'ignore' nodes above the mapchunk.
914                 while (vm->m_area.contains(above_remove_index) &&
915                                 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
916                                 vm->m_data[above_remove_index].getContent() != c_water_source &&
917                                 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
918                         vm->m_data[above_remove_index] = n_air;
919                         VoxelArea::add_y(em, above_remove_index, 1);
920                 }
921                 // Mud placed may have partially-buried a stacked decoration, search
922                 // above and remove.
923                 VoxelArea::add_y(em, place_index, 1);
924                 while (vm->m_area.contains(place_index) &&
925                                 vm->m_data[place_index].getContent() != CONTENT_AIR &&
926                                 vm->m_data[place_index].getContent() != c_water_source &&
927                                 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
928                         vm->m_data[place_index] = n_air;
929                         VoxelArea::add_y(em, place_index, 1);
930                 }
931         }
932 }
933
934
935 void MapgenV6::placeTreesAndJungleGrass()
936 {
937         //TimeTaker t("placeTrees");
938         if (node_max.Y < water_level)
939                 return;
940
941         PseudoRandom grassrandom(blockseed + 53);
942         content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
943         // if we don't have junglegrass, don't place cignore... that's bad
944         if (c_junglegrass == CONTENT_IGNORE)
945                 c_junglegrass = CONTENT_AIR;
946         MapNode n_junglegrass(c_junglegrass);
947         const v3s16 &em = vm->m_area.getExtent();
948
949         // Divide area into parts
950         s16 div = 8;
951         s16 sidelen = central_area_size.X / div;
952         double area = sidelen * sidelen;
953
954         // N.B.  We must add jungle grass first, since tree leaves will
955         // obstruct the ground, giving us a false ground level
956         for (s16 z0 = 0; z0 < div; z0++)
957         for (s16 x0 = 0; x0 < div; x0++) {
958                 // Center position of part of division
959                 v2s16 p2d_center(
960                         node_min.X + sidelen / 2 + sidelen * x0,
961                         node_min.Z + sidelen / 2 + sidelen * z0
962                 );
963                 // Minimum edge of part of division
964                 v2s16 p2d_min(
965                         node_min.X + sidelen * x0,
966                         node_min.Z + sidelen * z0
967                 );
968                 // Maximum edge of part of division
969                 v2s16 p2d_max(
970                         node_min.X + sidelen + sidelen * x0 - 1,
971                         node_min.Z + sidelen + sidelen * z0 - 1
972                 );
973
974                 // Get biome at center position of part of division
975                 BiomeV6Type bt = getBiome(p2d_center);
976
977                 // Amount of trees
978                 u32 tree_count;
979                 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
980                         tree_count = area * getTreeAmount(p2d_center);
981                         if (bt == BT_JUNGLE)
982                                 tree_count *= 4;
983                 } else {
984                         tree_count = 0;
985                 }
986
987                 // Add jungle grass
988                 if (bt == BT_JUNGLE) {
989                         float humidity = getHumidity(p2d_center);
990                         u32 grass_count = 5 * humidity * tree_count;
991                         for (u32 i = 0; i < grass_count; i++) {
992                                 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
993                                 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
994                                 int mapindex = central_area_size.X * (z - node_min.Z)
995                                                                 + (x - node_min.X);
996                                 s16 y = heightmap[mapindex];
997                                 if (y < water_level)
998                                         continue;
999
1000                                 u32 vi = vm->m_area.index(x, y, z);
1001                                 // place on dirt_with_grass, since we know it is exposed to sunlight
1002                                 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1003                                         VoxelArea::add_y(em, vi, 1);
1004                                         vm->m_data[vi] = n_junglegrass;
1005                                 }
1006                         }
1007                 }
1008
1009                 // Put trees in random places on part of division
1010                 for (u32 i = 0; i < tree_count; i++) {
1011                         s16 x = myrand_range(p2d_min.X, p2d_max.X);
1012                         s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1013                         int mapindex = central_area_size.X * (z - node_min.Z)
1014                                                         + (x - node_min.X);
1015                         s16 y = heightmap[mapindex];
1016                         // Don't make a tree under water level
1017                         // Don't make a tree so high that it doesn't fit
1018                         if (y < water_level || y > node_max.Y - 6)
1019                                 continue;
1020
1021                         v3s16 p(x, y, z);
1022                         // Trees grow only on mud and grass
1023                         {
1024                                 u32 i = vm->m_area.index(p);
1025                                 content_t c = vm->m_data[i].getContent();
1026                                 if (c != c_dirt &&
1027                                                 c != c_dirt_with_grass &&
1028                                                 c != c_dirt_with_snow)
1029                                         continue;
1030                         }
1031                         p.Y++;
1032
1033                         // Make a tree
1034                         if (bt == BT_JUNGLE) {
1035                                 treegen::make_jungletree(*vm, p, ndef, myrand());
1036                         } else if (bt == BT_TAIGA) {
1037                                 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1038                         } else if (bt == BT_NORMAL) {
1039                                 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1040                                                         getHaveAppleTree(v2s16(x, z));
1041                                 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1042                         }
1043                 }
1044         }
1045         //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1046 }
1047
1048
1049 void MapgenV6::growGrass() // Add surface nodes
1050 {
1051         MapNode n_dirt_with_grass(c_dirt_with_grass);
1052         MapNode n_dirt_with_snow(c_dirt_with_snow);
1053         MapNode n_snowblock(c_snowblock);
1054         MapNode n_snow(c_snow);
1055         const v3s16 &em = vm->m_area.getExtent();
1056
1057         u32 index = 0;
1058         for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1059         for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1060                 // Find the lowest surface to which enough light ends up to make
1061                 // grass grow.  Basically just wait until not air and not leaves.
1062                 s16 surface_y = 0;
1063                 {
1064                         u32 i = vm->m_area.index(x, node_max.Y, z);
1065                         s16 y;
1066                         // Go to ground level
1067                         for (y = node_max.Y; y >= full_node_min.Y; y--) {
1068                                 MapNode &n = vm->m_data[i];
1069                                 if (ndef->get(n).param_type != CPT_LIGHT ||
1070                                                 ndef->get(n).liquid_type != LIQUID_NONE ||
1071                                                 n.getContent() == c_ice)
1072                                         break;
1073                                 VoxelArea::add_y(em, i, -1);
1074                         }
1075                         surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1076                 }
1077
1078                 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1079                 u32 i = vm->m_area.index(x, surface_y, z);
1080                 content_t c = vm->m_data[i].getContent();
1081                 if (surface_y >= water_level - 20) {
1082                         if (bt == BT_TAIGA && c == c_dirt) {
1083                                 vm->m_data[i] = n_dirt_with_snow;
1084                         } else if (bt == BT_TUNDRA) {
1085                                 if (c == c_dirt) {
1086                                         vm->m_data[i] = n_snowblock;
1087                                         VoxelArea::add_y(em, i, -1);
1088                                         vm->m_data[i] = n_dirt_with_snow;
1089                                 } else if (c == c_stone && surface_y < node_max.Y) {
1090                                         VoxelArea::add_y(em, i, 1);
1091                                         vm->m_data[i] = n_snowblock;
1092                                 }
1093                         } else if (c == c_dirt) {
1094                                 vm->m_data[i] = n_dirt_with_grass;
1095                         }
1096                 }
1097         }
1098 }
1099
1100
1101 void MapgenV6::generateCaves(int max_stone_y)
1102 {
1103         float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1104         int volume_nodes = (node_max.X - node_min.X + 1) *
1105                                            (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1106         cave_amount = MYMAX(0.0, cave_amount);
1107         u32 caves_count = cave_amount * volume_nodes / 50000;
1108         u32 bruises_count = 1;
1109         PseudoRandom ps(blockseed + 21343);
1110         PseudoRandom ps2(blockseed + 1032);
1111
1112         if (ps.range(1, 6) == 1)
1113                 bruises_count = ps.range(0, ps.range(0, 2));
1114
1115         if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1116                 caves_count   /= 3;
1117                 bruises_count /= 3;
1118         }
1119
1120         for (u32 i = 0; i < caves_count + bruises_count; i++) {
1121                 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1122
1123                 bool large_cave = (i >= caves_count);
1124                 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1125                         large_cave, max_stone_y, heightmap);
1126         }
1127 }