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, U32_MAX);
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 //////////////////////// Some helper functions for the map generator
215 // Returns Y one under area minimum if not found
216 s16 MapgenV6::find_stone_level(v2s16 p2d)
218 const v3s16 &em = vm->m_area.getExtent();
219 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
220 s16 y_nodes_min = vm->m_area.MinEdge.Y;
221 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
224 for (y = y_nodes_max; y >= y_nodes_min; y--) {
225 content_t c = vm->m_data[i].getContent();
226 if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
229 VoxelArea::add_y(em, i, -1);
231 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
235 // Required by mapgen.h
236 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
238 /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
239 seed, v2s16(blockpos.X, blockpos.Z));*/
240 // Nah, this is just a heuristic, just return something
241 s16 minimum_groundlevel = water_level;
243 if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
250 //////////////////////// Base terrain height functions
252 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
253 float steepness, float height_select)
255 float base = 1 + terrain_base;
256 float higher = 1 + terrain_higher;
258 // Limit higher ground level to at least base
262 // Steepness factor of cliffs
264 b = rangelim(b, 0.0, 1000.0);
265 b = 5 * b * b * b * b * b * b * b;
266 b = rangelim(b, 0.5, 1000.0);
268 // Values 1.5...100 give quite horrible looking slopes
269 if (b > 1.5 && b < 100.0)
270 b = (b < 10.0) ? 1.5 : 100.0;
272 float a_off = -0.20; // Offset to more low
273 float a = 0.5 + b * (a_off + height_select);
274 a = rangelim(a, 0.0, 1.0); // Limit
276 return base * (1.0 - a) + higher * a;
280 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
282 if (spflags & MGV6_FLAT)
285 float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
286 p.X, 0.5, p.Y, 0.5, seed);
287 float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
288 p.X, 0.5, p.Y, 0.5, seed);
289 float steepness = NoisePerlin2D_PO(&noise_steepness->np,
290 p.X, 0.5, p.Y, 0.5, seed);
291 float height_select = NoisePerlin2D_PO(&noise_height_select->np,
292 p.X, 0.5, p.Y, 0.5, seed);
294 return baseTerrainLevel(terrain_base, terrain_higher,
295 steepness, height_select);
299 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
301 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
302 return baseTerrainLevelFromMap(index);
306 float MapgenV6::baseTerrainLevelFromMap(int index)
308 if (spflags & MGV6_FLAT)
311 float terrain_base = noise_terrain_base->result[index];
312 float terrain_higher = noise_terrain_higher->result[index];
313 float steepness = noise_steepness->result[index];
314 float height_select = noise_height_select->result[index];
316 return baseTerrainLevel(terrain_base, terrain_higher,
317 steepness, height_select);
321 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
323 return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
327 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
329 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
333 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
335 s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
336 if (level_at_point <= water_level ||
337 level_at_point > water_level + 16)
338 return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
340 return level_at_point;
344 //////////////////////// Noise functions
346 float MapgenV6::getMudAmount(v2s16 p)
348 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
349 return getMudAmount(index);
353 bool MapgenV6::getHaveBeach(v2s16 p)
355 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
356 return getHaveBeach(index);
360 BiomeV6Type MapgenV6::getBiome(v2s16 p)
362 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
363 + (p.X - full_node_min.X);
364 return getBiome(index, p);
368 float MapgenV6::getHumidity(v2s16 p)
370 /*double noise = noise2d_perlin(
371 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
372 seed+72384, 4, 0.66);
373 noise = (noise + 1.0)/2.0;*/
375 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
376 + (p.X - full_node_min.X);
377 float noise = noise_humidity->result[index];
387 float MapgenV6::getTreeAmount(v2s16 p)
389 /*double noise = noise2d_perlin(
390 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
393 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
394 float zeroval = -0.39;
398 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
402 bool MapgenV6::getHaveAppleTree(v2s16 p)
404 /*is_apple_tree = noise2d_perlin(
405 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
406 data->seed+342902, 3, 0.45) > 0.2;*/
408 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
414 float MapgenV6::getMudAmount(int index)
416 if (spflags & MGV6_FLAT)
417 return MGV6_AVERAGE_MUD_AMOUNT;
419 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
420 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
421 seed+91013, 3, 0.55));*/
423 return noise_mud->result[index];
427 bool MapgenV6::getHaveBeach(int index)
429 // Determine whether to have sand here
430 /*double sandnoise = noise2d_perlin(
431 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
432 seed+59420, 3, 0.50);*/
434 float sandnoise = noise_beach->result[index];
435 return (sandnoise > freq_beach);
439 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
441 // Just do something very simple as for now
442 /*double d = noise2d_perlin(
443 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
444 seed+9130, 3, 0.50);*/
446 float d = noise_biome->result[index];
447 float h = noise_humidity->result[index];
449 if (spflags & MGV6_SNOWBIOMES) {
450 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
452 if (d > MGV6_FREQ_HOT + blend) {
453 if (h > MGV6_FREQ_JUNGLE + blend)
459 if (d < MGV6_FREQ_SNOW + blend) {
460 if (h > MGV6_FREQ_TAIGA + blend)
472 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
473 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
476 if ((spflags & MGV6_JUNGLES) && h > 0.75)
484 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
486 s32 x = p.X, y = p.Y, z = p.Z;
487 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
491 //////////////////////// Map generator
493 void MapgenV6::makeChunk(BlockMakeData *data)
496 assert(data->vmanip);
497 assert(data->nodedef);
498 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
499 data->blockpos_requested.Y >= data->blockpos_min.Y &&
500 data->blockpos_requested.Z >= data->blockpos_min.Z);
501 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
502 data->blockpos_requested.Y <= data->blockpos_max.Y &&
503 data->blockpos_requested.Z <= data->blockpos_max.Z);
505 this->generating = true;
506 this->vm = data->vmanip;
507 this->ndef = data->nodedef;
509 // Hack: use minimum block coords for old code that assumes a single block
510 v3s16 blockpos_min = data->blockpos_min;
511 v3s16 blockpos_max = data->blockpos_max;
513 // Area of central chunk
514 node_min = blockpos_min * MAP_BLOCKSIZE;
515 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
517 // Full allocated area
518 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
519 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
521 central_area_size = node_max - node_min + v3s16(1, 1, 1);
522 assert(central_area_size.X == central_area_size.Z);
524 // Create a block-specific seed
525 blockseed = get_blockseed(data->seed, full_node_min);
530 // Maximum height of the stone surface and obstacles.
531 // This is used to guide the cave generation
532 s16 stone_surface_max_y;
534 // Generate general ground level to full area
535 stone_surface_max_y = generateGround();
537 // Create initial heightmap to limit caves
538 updateHeightmap(node_min, node_max);
540 const s16 max_spread_amount = MAP_BLOCKSIZE;
541 // Limit dirt flow area by 1 because mud is flown into neighbors.
542 s16 mudflow_minpos = -max_spread_amount + 1;
543 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
545 // Loop this part, it will make stuff look older and newer nicely
546 const u32 age_loops = 2;
547 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
548 // Make caves (this code is relatively horrible)
549 if (flags & MG_CAVES)
550 generateCaves(stone_surface_max_y);
552 // Add mud to the central chunk
555 // Flow mud away from steep edges
556 if (spflags & MGV6_MUDFLOW)
557 flowMud(mudflow_minpos, mudflow_maxpos);
561 // Update heightmap after mudflow
562 updateHeightmap(node_min, node_max);
565 if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
566 full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
567 u16 num_dungeons = std::fmax(std::floor(
568 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
570 if (num_dungeons >= 1) {
571 PseudoRandom ps(blockseed + 4713);
576 dp.num_dungeons = num_dungeons;
577 dp.only_in_ground = true;
578 dp.corridor_len_min = 1;
579 dp.corridor_len_max = 13;
580 dp.num_rooms = ps.range(2, 16);
581 dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
584 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
586 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
587 dp.c_wall = c_desert_stone;
588 dp.c_alt_wall = CONTENT_IGNORE;
589 dp.c_stair = c_stair_desert_stone;
591 dp.diagonal_dirs = true;
592 dp.holesize = v3s16(2, 3, 2);
593 dp.room_size_min = v3s16(6, 9, 6);
594 dp.room_size_max = v3s16(10, 11, 10);
595 dp.room_size_large_min = v3s16(10, 13, 10);
596 dp.room_size_large_max = v3s16(18, 21, 18);
597 dp.notifytype = GENNOTIFY_TEMPLE;
599 dp.c_wall = c_cobble;
600 dp.c_alt_wall = c_mossycobble;
601 dp.c_stair = c_stair_cobble;
603 dp.diagonal_dirs = false;
604 dp.holesize = v3s16(1, 2, 1);
605 dp.room_size_min = v3s16(4, 4, 4);
606 dp.room_size_max = v3s16(8, 6, 8);
607 dp.room_size_large_min = v3s16(8, 8, 8);
608 dp.room_size_large_max = v3s16(16, 16, 16);
609 dp.notifytype = GENNOTIFY_DUNGEON;
612 DungeonGen dgen(ndef, &gennotify, &dp);
613 dgen.generate(vm, blockseed, full_node_min, full_node_max);
617 // Add top and bottom side of water to transforming_liquid queue
618 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
623 // Generate some trees, and add grass, if a jungle
624 if (spflags & MGV6_TREES)
625 placeTreesAndJungleGrass();
627 // Generate the registered decorations
628 if (flags & MG_DECORATIONS)
629 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
631 // Generate the registered ores
632 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
634 // Calculate lighting
635 if (flags & MG_LIGHT)
636 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
637 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
638 full_node_min, full_node_max);
640 this->generating = false;
644 void MapgenV6::calculateNoise()
648 int fx = full_node_min.X;
649 int fz = full_node_min.Z;
651 if (!(spflags & MGV6_FLAT)) {
652 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
653 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
654 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
655 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
656 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
659 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
661 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
662 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
663 // Humidity map does not need range limiting 0 to 1,
664 // only humidity at point does
668 int MapgenV6::generateGround()
670 //TimeTaker timer1("Generating ground level");
671 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
672 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
673 MapNode n_ice(c_ice);
674 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
677 for (s16 z = node_min.Z; z <= node_max.Z; z++)
678 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
680 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
683 if (surface_y > stone_surface_max_y)
684 stone_surface_max_y = surface_y;
686 BiomeV6Type bt = getBiome(v2s16(x, z));
688 // Fill ground with stone
689 const v3s16 &em = vm->m_area.getExtent();
690 u32 i = vm->m_area.index(x, node_min.Y, z);
691 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
692 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
693 if (y <= surface_y) {
694 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
695 && bt == BT_DESERT) ?
696 n_desert_stone : n_stone;
697 } else if (y <= water_level) {
698 vm->m_data[i] = (y >= MGV6_ICE_BASE
699 && bt == BT_TUNDRA) ?
700 n_ice : n_water_source;
702 vm->m_data[i] = n_air;
705 VoxelArea::add_y(em, i, 1);
709 return stone_surface_max_y;
713 void MapgenV6::addMud()
716 //TimeTaker timer1("add mud");
717 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
718 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
722 for (s16 z = node_min.Z; z <= node_max.Z; z++)
723 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
724 // Randomize mud amount
725 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
728 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
730 // Handle area not found
731 if (surface_y == vm->m_area.MinEdge.Y - 1)
734 BiomeV6Type bt = getBiome(v2s16(x, z));
735 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
737 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
739 } else if (mud_add_amount <= 0) {
740 mud_add_amount = 1 - mud_add_amount;
742 } else if (bt != BT_DESERT && getHaveBeach(index) &&
743 surface_y + mud_add_amount <= water_level + 2) {
747 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
748 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
750 /* If topmost node is grass, change it to mud. It might be if it was
751 // flown to there from a neighboring chunk and then converted.
752 u32 i = vm->m_area.index(x, surface_y, z);
753 if (vm->m_data[i].getContent() == c_dirt_with_grass)
754 vm->m_data[i] = n_dirt;*/
758 const v3s16 &em = vm->m_area.getExtent();
759 s16 y_start = surface_y + 1;
760 u32 i = vm->m_area.index(x, y_start, z);
761 for (s16 y = y_start; y <= node_max.Y; y++) {
762 if (mudcount >= mud_add_amount)
765 vm->m_data[i] = addnode;
768 VoxelArea::add_y(em, i, 1);
774 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
777 //TimeTaker timer1("flow mud");
779 // Iterate a few times
780 for (s16 k = 0; k < 3; k++) {
781 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
782 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
783 // Invert coordinates every 2nd iteration
785 x = mudflow_maxpos - (x - mudflow_minpos);
786 z = mudflow_maxpos - (z - mudflow_minpos);
789 // Node position in 2d
790 v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
792 const v3s16 &em = vm->m_area.getExtent();
793 u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
796 while (y >= node_min.Y) {
801 for (; y >= node_min.Y; y--) {
803 if (n->getContent() == c_dirt ||
804 n->getContent() == c_dirt_with_grass ||
805 n->getContent() == c_gravel)
808 VoxelArea::add_y(em, i, -1);
811 // Stop if out of area
812 //if(vmanip.m_area.contains(i) == false)
816 if (n->getContent() == c_dirt ||
817 n->getContent() == c_dirt_with_grass) {
818 // Make it exactly mud
819 n->setContent(c_dirt);
821 // Don't flow it if the stuff under it is not mud
824 VoxelArea::add_y(em, i2, -1);
825 // Cancel if out of area
826 if (!vm->m_area.contains(i2))
828 MapNode *n2 = &vm->m_data[i2];
829 if (n2->getContent() != c_dirt &&
830 n2->getContent() != c_dirt_with_grass)
835 static const v3s16 dirs4[4] = {
836 v3s16(0, 0, 1), // back
837 v3s16(1, 0, 0), // right
838 v3s16(0, 0, -1), // front
839 v3s16(-1, 0, 0), // left
842 // Check that upper is walkable. Cancel
843 // dropping if upper keeps it in place.
845 VoxelArea::add_y(em, i3, 1);
848 if (vm->m_area.contains(i3)) {
849 n3 = &vm->m_data[i3];
850 if (ndef->get(*n3).walkable)
855 for (const v3s16 &dirp : dirs4) {
858 VoxelArea::add_p(em, i2, dirp);
859 // Fail if out of area
860 if (!vm->m_area.contains(i2))
862 // Check that side is air
863 MapNode *n2 = &vm->m_data[i2];
864 if (ndef->get(*n2).walkable)
866 // Check that under side is air
867 VoxelArea::add_y(em, i2, -1);
868 if (!vm->m_area.contains(i2))
870 n2 = &vm->m_data[i2];
871 if (ndef->get(*n2).walkable)
873 // Loop further down until not air
874 bool dropped_to_unknown = false;
876 VoxelArea::add_y(em, i2, -1);
877 n2 = &vm->m_data[i2];
878 // if out of known area
879 if (!vm->m_area.contains(i2) ||
880 n2->getContent() == CONTENT_IGNORE) {
881 dropped_to_unknown = true;
884 } while (!ndef->get(*n2).walkable);
885 // Loop one up so that we're in air
886 VoxelArea::add_y(em, i2, 1);
888 // Move mud to new place. Outside mapchunk remove
889 // any decorations above removed or placed mud.
890 if (!dropped_to_unknown)
891 moveMud(i, i2, i3, p2d, em);
903 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
904 u32 above_remove_index, v2s16 pos, v3s16 em)
906 MapNode n_air(CONTENT_AIR);
907 // Copy mud from old place to new place
908 vm->m_data[place_index] = vm->m_data[remove_index];
909 // Set old place to be air
910 vm->m_data[remove_index] = n_air;
911 // Outside the mapchunk decorations may need to be removed if above removed
912 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
913 // use 'pos.X >= node_max.X' etc.
914 if (pos.X >= node_max.X || pos.X <= node_min.X ||
915 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
916 // 'above remove' node is above removed mud. If it is not air, water or
917 // 'ignore' it is a decoration that needs removing. Also search upwards
918 // to remove a possible stacked decoration.
919 // Check for 'ignore' because stacked decorations can penetrate into
920 // 'ignore' nodes above the mapchunk.
921 while (vm->m_area.contains(above_remove_index) &&
922 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
923 vm->m_data[above_remove_index].getContent() != c_water_source &&
924 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
925 vm->m_data[above_remove_index] = n_air;
926 VoxelArea::add_y(em, above_remove_index, 1);
928 // Mud placed may have partially-buried a stacked decoration, search
930 VoxelArea::add_y(em, place_index, 1);
931 while (vm->m_area.contains(place_index) &&
932 vm->m_data[place_index].getContent() != CONTENT_AIR &&
933 vm->m_data[place_index].getContent() != c_water_source &&
934 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
935 vm->m_data[place_index] = n_air;
936 VoxelArea::add_y(em, place_index, 1);
942 void MapgenV6::placeTreesAndJungleGrass()
944 //TimeTaker t("placeTrees");
945 if (node_max.Y < water_level)
948 PseudoRandom grassrandom(blockseed + 53);
949 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
950 // if we don't have junglegrass, don't place cignore... that's bad
951 if (c_junglegrass == CONTENT_IGNORE)
952 c_junglegrass = CONTENT_AIR;
953 MapNode n_junglegrass(c_junglegrass);
954 const v3s16 &em = vm->m_area.getExtent();
956 // Divide area into parts
958 s16 sidelen = central_area_size.X / div;
959 double area = sidelen * sidelen;
961 // N.B. We must add jungle grass first, since tree leaves will
962 // obstruct the ground, giving us a false ground level
963 for (s16 z0 = 0; z0 < div; z0++)
964 for (s16 x0 = 0; x0 < div; x0++) {
965 // Center position of part of division
967 node_min.X + sidelen / 2 + sidelen * x0,
968 node_min.Z + sidelen / 2 + sidelen * z0
970 // Minimum edge of part of division
972 node_min.X + sidelen * x0,
973 node_min.Z + sidelen * z0
975 // Maximum edge of part of division
977 node_min.X + sidelen + sidelen * x0 - 1,
978 node_min.Z + sidelen + sidelen * z0 - 1
981 // Get biome at center position of part of division
982 BiomeV6Type bt = getBiome(p2d_center);
986 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
987 tree_count = area * getTreeAmount(p2d_center);
995 if (bt == BT_JUNGLE) {
996 float humidity = getHumidity(p2d_center);
997 u32 grass_count = 5 * humidity * tree_count;
998 for (u32 i = 0; i < grass_count; i++) {
999 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
1000 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
1001 int mapindex = central_area_size.X * (z - node_min.Z)
1003 s16 y = heightmap[mapindex];
1004 if (y < water_level)
1007 u32 vi = vm->m_area.index(x, y, z);
1008 // place on dirt_with_grass, since we know it is exposed to sunlight
1009 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1010 VoxelArea::add_y(em, vi, 1);
1011 vm->m_data[vi] = n_junglegrass;
1016 // Put trees in random places on part of division
1017 for (u32 i = 0; i < tree_count; i++) {
1018 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1019 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1020 int mapindex = central_area_size.X * (z - node_min.Z)
1022 s16 y = heightmap[mapindex];
1023 // Don't make a tree under water level
1024 // Don't make a tree so high that it doesn't fit
1025 if (y < water_level || y > node_max.Y - 6)
1029 // Trees grow only on mud and grass
1031 u32 i = vm->m_area.index(p);
1032 content_t c = vm->m_data[i].getContent();
1034 c != c_dirt_with_grass &&
1035 c != c_dirt_with_snow)
1041 if (bt == BT_JUNGLE) {
1042 treegen::make_jungletree(*vm, p, ndef, myrand());
1043 } else if (bt == BT_TAIGA) {
1044 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1045 } else if (bt == BT_NORMAL) {
1046 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1047 getHaveAppleTree(v2s16(x, z));
1048 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1052 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1056 void MapgenV6::growGrass() // Add surface nodes
1058 MapNode n_dirt_with_grass(c_dirt_with_grass);
1059 MapNode n_dirt_with_snow(c_dirt_with_snow);
1060 MapNode n_snowblock(c_snowblock);
1061 MapNode n_snow(c_snow);
1062 const v3s16 &em = vm->m_area.getExtent();
1065 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1066 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1067 // Find the lowest surface to which enough light ends up to make
1068 // grass grow. Basically just wait until not air and not leaves.
1071 u32 i = vm->m_area.index(x, node_max.Y, z);
1073 // Go to ground level
1074 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1075 MapNode &n = vm->m_data[i];
1076 if (ndef->get(n).param_type != CPT_LIGHT ||
1077 ndef->get(n).liquid_type != LIQUID_NONE ||
1078 n.getContent() == c_ice)
1080 VoxelArea::add_y(em, i, -1);
1082 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1085 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1086 u32 i = vm->m_area.index(x, surface_y, z);
1087 content_t c = vm->m_data[i].getContent();
1088 if (surface_y >= water_level - 20) {
1089 if (bt == BT_TAIGA && c == c_dirt) {
1090 vm->m_data[i] = n_dirt_with_snow;
1091 } else if (bt == BT_TUNDRA) {
1093 vm->m_data[i] = n_snowblock;
1094 VoxelArea::add_y(em, i, -1);
1095 vm->m_data[i] = n_dirt_with_snow;
1096 } else if (c == c_stone && surface_y < node_max.Y) {
1097 VoxelArea::add_y(em, i, 1);
1098 vm->m_data[i] = n_snowblock;
1100 } else if (c == c_dirt) {
1101 vm->m_data[i] = n_dirt_with_grass;
1108 void MapgenV6::generateCaves(int max_stone_y)
1110 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1111 int volume_nodes = (node_max.X - node_min.X + 1) *
1112 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1113 cave_amount = MYMAX(0.0, cave_amount);
1114 u32 caves_count = cave_amount * volume_nodes / 50000;
1115 u32 bruises_count = 1;
1116 PseudoRandom ps(blockseed + 21343);
1117 PseudoRandom ps2(blockseed + 1032);
1119 if (ps.range(1, 6) == 1)
1120 bruises_count = ps.range(0, ps.range(0, 2));
1122 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1127 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1128 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1130 bool large_cave = (i >= caves_count);
1131 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1132 large_cave, max_stone_y, heightmap);