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
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.
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.
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.
30 //#include "serverobject.h"
31 #include "content_sao.h"
33 #include "voxelalgorithms.h"
34 //#include "profiler.h" // For TimeTaker
35 #include "settings.h" // For g_settings
37 #include "dungeongen.h"
41 #include "mg_decoration.h"
42 #include "mapgen_v6.h"
45 FlagDesc flagdesc_mapgen_v6[] = {
46 {"jungles", MGV6_JUNGLES},
47 {"biomeblend", MGV6_BIOMEBLEND},
48 {"mudflow", MGV6_MUDFLOW},
49 {"snowbiomes", MGV6_SNOWBIOMES},
51 {"trees", MGV6_TREES},
56 /////////////////////////////////////////////////////////////////////////////
59 MapgenV6::MapgenV6(MapgenV6Params *params, EmergeManager *emerge)
60 : Mapgen(MAPGEN_V6, params, emerge)
65 heightmap = new s16[csize.X * csize.Z];
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;
73 np_cave = ¶ms->np_cave;
74 np_humidity = ¶ms->np_humidity;
75 np_trees = ¶ms->np_trees;
76 np_apple_trees = ¶ms->np_apple_trees;
78 np_dungeons = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
80 //// Create noise objects
81 noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Y);
82 noise_terrain_higher = new Noise(¶ms->np_terrain_higher, seed, csize.X, csize.Y);
83 noise_steepness = new Noise(¶ms->np_steepness, seed, csize.X, csize.Y);
84 noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Y);
85 noise_mud = new Noise(¶ms->np_mud, seed, csize.X, csize.Y);
86 noise_beach = new Noise(¶ms->np_beach, seed, csize.X, csize.Y);
87 noise_biome = new Noise(¶ms->np_biome, seed,
88 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
89 noise_humidity = new Noise(¶ms->np_humidity, seed,
90 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
92 //// Resolve nodes to be used
93 const NodeDefManager *ndef = emerge->ndef;
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");
109 if (c_gravel == CONTENT_IGNORE)
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;
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");
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;
138 MapgenV6::~MapgenV6()
140 delete noise_terrain_base;
141 delete noise_terrain_higher;
142 delete noise_steepness;
143 delete noise_height_select;
147 delete noise_humidity;
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)
169 void MapgenV6Params::readParams(const Settings *settings)
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);
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);
191 void MapgenV6Params::writeParams(Settings *settings) const
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);
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);
213 void MapgenV6Params::setDefaultSettings(Settings *settings)
215 settings->setDefault("mgv6_spflags", flagdesc_mapgen_v6, MGV6_JUNGLES |
216 MGV6_SNOWBIOMES | MGV6_TREES | MGV6_BIOMEBLEND | MGV6_MUDFLOW);
220 //////////////////////// Some helper functions for the map generator
223 // Returns Y one under area minimum if not found
224 s16 MapgenV6::find_stone_level(v2s16 p2d)
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);
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))
237 VoxelArea::add_y(em, i, -1);
239 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
243 // Required by mapgen.h
244 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
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;
251 if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
258 //////////////////////// Base terrain height functions
260 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
261 float steepness, float height_select)
263 float base = 1 + terrain_base;
264 float higher = 1 + terrain_higher;
266 // Limit higher ground level to at least base
270 // Steepness factor of cliffs
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);
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;
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
284 return base * (1.0 - a) + higher * a;
288 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
290 if (spflags & MGV6_FLAT)
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);
302 return baseTerrainLevel(terrain_base, terrain_higher,
303 steepness, height_select);
307 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
309 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
310 return baseTerrainLevelFromMap(index);
314 float MapgenV6::baseTerrainLevelFromMap(int index)
316 if (spflags & MGV6_FLAT)
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];
324 return baseTerrainLevel(terrain_base, terrain_higher,
325 steepness, height_select);
329 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
331 return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
335 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
337 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
341 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
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
348 return level_at_point;
352 //////////////////////// Noise functions
354 float MapgenV6::getMudAmount(v2s16 p)
356 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
357 return getMudAmount(index);
361 bool MapgenV6::getHaveBeach(v2s16 p)
363 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
364 return getHaveBeach(index);
368 BiomeV6Type MapgenV6::getBiome(v2s16 p)
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);
376 float MapgenV6::getHumidity(v2s16 p)
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;*/
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];
395 float MapgenV6::getTreeAmount(v2s16 p)
397 /*double noise = noise2d_perlin(
398 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
401 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
402 float zeroval = -0.39;
406 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
410 bool MapgenV6::getHaveAppleTree(v2s16 p)
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;*/
416 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
422 float MapgenV6::getMudAmount(int index)
424 if (spflags & MGV6_FLAT)
425 return MGV6_AVERAGE_MUD_AMOUNT;
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));*/
431 return noise_mud->result[index];
435 bool MapgenV6::getHaveBeach(int index)
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);*/
442 float sandnoise = noise_beach->result[index];
443 return (sandnoise > freq_beach);
447 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
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);*/
454 float d = noise_biome->result[index];
455 float h = noise_humidity->result[index];
457 if (spflags & MGV6_SNOWBIOMES) {
458 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
460 if (d > MGV6_FREQ_HOT + blend) {
461 if (h > MGV6_FREQ_JUNGLE + blend)
467 if (d < MGV6_FREQ_SNOW + blend) {
468 if (h > MGV6_FREQ_TAIGA + blend)
480 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
481 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
484 if ((spflags & MGV6_JUNGLES) && h > 0.75)
492 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
494 s32 x = p.X, y = p.Y, z = p.Z;
495 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
499 //////////////////////// Map generator
501 void MapgenV6::makeChunk(BlockMakeData *data)
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);
513 this->generating = true;
514 this->vm = data->vmanip;
515 this->ndef = data->nodedef;
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;
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);
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);
529 central_area_size = node_max - node_min + v3s16(1, 1, 1);
530 assert(central_area_size.X == central_area_size.Z);
532 // Create a block-specific seed
533 blockseed = get_blockseed(data->seed, full_node_min);
538 // Maximum height of the stone surface and obstacles.
539 // This is used to guide the cave generation
540 s16 stone_surface_max_y;
542 // Generate general ground level to full area
543 stone_surface_max_y = generateGround();
545 // Create initial heightmap to limit caves
546 updateHeightmap(node_min, node_max);
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;
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);
560 // Add mud to the central chunk
563 // Flow mud away from steep edges
564 if (spflags & MGV6_MUDFLOW)
565 flowMud(mudflow_minpos, mudflow_maxpos);
569 // Update heightmap after mudflow
570 updateHeightmap(node_min, node_max);
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);
578 if (num_dungeons >= 1) {
579 PseudoRandom ps(blockseed + 4713);
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;
592 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
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;
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;
607 dp.c_wall = c_cobble;
608 dp.c_alt_wall = c_mossycobble;
609 dp.c_stair = c_stair_cobble;
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;
620 DungeonGen dgen(ndef, &gennotify, &dp);
621 dgen.generate(vm, blockseed, full_node_min, full_node_max);
625 // Add top and bottom side of water to transforming_liquid queue
626 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
631 // Generate some trees, and add grass, if a jungle
632 if (spflags & MGV6_TREES)
633 placeTreesAndJungleGrass();
635 // Generate the registered decorations
636 if (flags & MG_DECORATIONS)
637 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
639 // Generate the registered ores
640 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
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);
648 this->generating = false;
652 void MapgenV6::calculateNoise()
656 int fx = full_node_min.X;
657 int fz = full_node_min.Z;
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);
667 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
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
676 int MapgenV6::generateGround()
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;
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++) {
688 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
691 if (surface_y > stone_surface_max_y)
692 stone_surface_max_y = surface_y;
694 BiomeV6Type bt = getBiome(v2s16(x, z));
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;
710 vm->m_data[i] = n_air;
713 VoxelArea::add_y(em, i, 1);
717 return stone_surface_max_y;
721 void MapgenV6::addMud()
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);
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;
736 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
738 // Handle area not found
739 if (surface_y == vm->m_area.MinEdge.Y - 1)
742 BiomeV6Type bt = getBiome(v2s16(x, z));
743 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
745 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
747 } else if (mud_add_amount <= 0) {
748 mud_add_amount = 1 - mud_add_amount;
750 } else if (bt != BT_DESERT && getHaveBeach(index) &&
751 surface_y + mud_add_amount <= water_level + 2) {
755 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
756 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
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;*/
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)
773 vm->m_data[i] = addnode;
776 VoxelArea::add_y(em, i, 1);
782 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
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
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
798 // Invert coordinates on second iteration to process columns in
799 // opposite order, to avoid a directional bias.
801 p2d = v2s16(node_max.X, node_max.Z) - v2s16(x, z);
803 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
807 while (y >= node_min.Y) {
809 u32 i = vm->m_area.index(p2d.X, y, p2d.Y);
810 MapNode *n = nullptr;
812 // Find next mud node in mapchunk column
813 for (; y >= node_min.Y; y--) {
815 if (n->getContent() == c_dirt ||
816 n->getContent() == c_dirt_with_grass ||
817 n->getContent() == c_gravel)
820 VoxelArea::add_y(em, i, -1);
823 // No mud found in mapchunk column, process the next column
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.
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
840 // Check if node above is walkable. If so, cancel
841 // flowing as if node above keeps it in place.
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
849 // Drop mud on one side
850 for (const v3s16 &dirp : dirs4) {
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)
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)
865 // Loop further down until not air
866 s16 y2 = y - 1; // y of i2
867 bool dropped_to_unknown = false;
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;
877 } while (!ndef->get(*n2).walkable);
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);
886 // Done, find next mud node in column
896 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
897 u32 above_remove_index, v2s16 pos, v3s16 em)
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);
921 // Mud placed may have partially-buried a stacked decoration, search
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);
935 void MapgenV6::placeTreesAndJungleGrass()
937 //TimeTaker t("placeTrees");
938 if (node_max.Y < water_level)
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();
949 // Divide area into parts
951 s16 sidelen = central_area_size.X / div;
952 double area = sidelen * sidelen;
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
960 node_min.X + sidelen / 2 + sidelen * x0,
961 node_min.Z + sidelen / 2 + sidelen * z0
963 // Minimum edge of part of division
965 node_min.X + sidelen * x0,
966 node_min.Z + sidelen * z0
968 // Maximum edge of part of division
970 node_min.X + sidelen + sidelen * x0 - 1,
971 node_min.Z + sidelen + sidelen * z0 - 1
974 // Get biome at center position of part of division
975 BiomeV6Type bt = getBiome(p2d_center);
979 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
980 tree_count = area * getTreeAmount(p2d_center);
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)
996 s16 y = heightmap[mapindex];
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;
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)
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)
1022 // Trees grow only on mud and grass
1024 u32 i = vm->m_area.index(p);
1025 content_t c = vm->m_data[i].getContent();
1027 c != c_dirt_with_grass &&
1028 c != c_dirt_with_snow)
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());
1045 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1049 void MapgenV6::growGrass() // Add surface nodes
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();
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.
1064 u32 i = vm->m_area.index(x, node_max.Y, z);
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)
1073 VoxelArea::add_y(em, i, -1);
1075 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
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) {
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;
1093 } else if (c == c_dirt) {
1094 vm->m_data[i] = n_dirt_with_grass;
1101 void MapgenV6::generateCaves(int max_stone_y)
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);
1112 if (ps.range(1, 6) == 1)
1113 bruises_count = ps.range(0, ps.range(0, 2));
1115 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1120 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1121 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1123 bool large_cave = (i >= caves_count);
1124 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1125 large_cave, max_stone_y, heightmap);