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