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.
29 //#include "serverobject.h"
30 #include "content_sao.h"
32 #include "voxelalgorithms.h"
33 //#include "profiler.h" // For TimeTaker
34 #include "settings.h" // For g_settings
36 #include "dungeongen.h"
40 #include "mg_decoration.h"
41 #include "mapgen_v6.h"
44 FlagDesc flagdesc_mapgen_v6[] = {
45 {"jungles", MGV6_JUNGLES},
46 {"biomeblend", MGV6_BIOMEBLEND},
47 {"mudflow", MGV6_MUDFLOW},
48 {"snowbiomes", MGV6_SNOWBIOMES},
50 {"trees", MGV6_TREES},
55 /////////////////////////////////////////////////////////////////////////////
58 MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge)
59 : Mapgen(mapgenid, params, emerge)
62 ystride = csize.X; //////fix this
64 heightmap = new s16[csize.X * csize.Z];
66 spflags = params->spflags;
67 freq_desert = params->freq_desert;
68 freq_beach = params->freq_beach;
69 dungeon_ymin = params->dungeon_ymin;
70 dungeon_ymax = params->dungeon_ymax;
72 np_cave = ¶ms->np_cave;
73 np_humidity = ¶ms->np_humidity;
74 np_trees = ¶ms->np_trees;
75 np_apple_trees = ¶ms->np_apple_trees;
77 //// Create noise objects
78 noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Y);
79 noise_terrain_higher = new Noise(¶ms->np_terrain_higher, seed, csize.X, csize.Y);
80 noise_steepness = new Noise(¶ms->np_steepness, seed, csize.X, csize.Y);
81 noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Y);
82 noise_mud = new Noise(¶ms->np_mud, seed, csize.X, csize.Y);
83 noise_beach = new Noise(¶ms->np_beach, seed, csize.X, csize.Y);
84 noise_biome = new Noise(¶ms->np_biome, seed,
85 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
86 noise_humidity = new Noise(¶ms->np_humidity, seed,
87 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
89 //// Resolve nodes to be used
90 const NodeDefManager *ndef = emerge->ndef;
92 c_stone = ndef->getId("mapgen_stone");
93 c_dirt = ndef->getId("mapgen_dirt");
94 c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
95 c_sand = ndef->getId("mapgen_sand");
96 c_water_source = ndef->getId("mapgen_water_source");
97 c_lava_source = ndef->getId("mapgen_lava_source");
98 c_gravel = ndef->getId("mapgen_gravel");
99 c_desert_stone = ndef->getId("mapgen_desert_stone");
100 c_desert_sand = ndef->getId("mapgen_desert_sand");
101 c_dirt_with_snow = ndef->getId("mapgen_dirt_with_snow");
102 c_snow = ndef->getId("mapgen_snow");
103 c_snowblock = ndef->getId("mapgen_snowblock");
104 c_ice = ndef->getId("mapgen_ice");
106 if (c_gravel == CONTENT_IGNORE)
108 if (c_desert_stone == CONTENT_IGNORE)
109 c_desert_stone = c_stone;
110 if (c_desert_sand == CONTENT_IGNORE)
111 c_desert_sand = c_sand;
112 if (c_dirt_with_snow == CONTENT_IGNORE)
113 c_dirt_with_snow = c_dirt_with_grass;
114 if (c_snow == CONTENT_IGNORE)
115 c_snow = CONTENT_AIR;
116 if (c_snowblock == CONTENT_IGNORE)
117 c_snowblock = c_dirt_with_grass;
118 if (c_ice == CONTENT_IGNORE)
119 c_ice = c_water_source;
121 c_cobble = ndef->getId("mapgen_cobble");
122 c_mossycobble = ndef->getId("mapgen_mossycobble");
123 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
124 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
126 if (c_mossycobble == CONTENT_IGNORE)
127 c_mossycobble = c_cobble;
128 if (c_stair_cobble == CONTENT_IGNORE)
129 c_stair_cobble = c_cobble;
130 if (c_stair_desert_stone == CONTENT_IGNORE)
131 c_stair_desert_stone = c_desert_stone;
135 MapgenV6::~MapgenV6()
137 delete noise_terrain_base;
138 delete noise_terrain_higher;
139 delete noise_steepness;
140 delete noise_height_select;
144 delete noise_humidity;
150 MapgenV6Params::MapgenV6Params():
151 np_terrain_base (-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0),
152 np_terrain_higher (20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0),
153 np_steepness (0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0),
154 np_height_select (0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0),
155 np_mud (4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0),
156 np_beach (0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0),
157 np_biome (0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0),
158 np_cave (6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0),
159 np_humidity (0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0),
160 np_trees (0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0),
161 np_apple_trees (0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
166 void MapgenV6Params::readParams(const Settings *settings)
168 settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
169 settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
170 settings->getFloatNoEx("mgv6_freq_beach", freq_beach);
171 settings->getS16NoEx("mgv6_dungeon_ymin", dungeon_ymin);
172 settings->getS16NoEx("mgv6_dungeon_ymax", dungeon_ymax);
174 settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base);
175 settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
176 settings->getNoiseParams("mgv6_np_steepness", np_steepness);
177 settings->getNoiseParams("mgv6_np_height_select", np_height_select);
178 settings->getNoiseParams("mgv6_np_mud", np_mud);
179 settings->getNoiseParams("mgv6_np_beach", np_beach);
180 settings->getNoiseParams("mgv6_np_biome", np_biome);
181 settings->getNoiseParams("mgv6_np_cave", np_cave);
182 settings->getNoiseParams("mgv6_np_humidity", np_humidity);
183 settings->getNoiseParams("mgv6_np_trees", np_trees);
184 settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees);
188 void MapgenV6Params::writeParams(Settings *settings) const
190 settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
191 settings->setFloat("mgv6_freq_desert", freq_desert);
192 settings->setFloat("mgv6_freq_beach", freq_beach);
193 settings->setS16("mgv6_dungeon_ymin", dungeon_ymin);
194 settings->setS16("mgv6_dungeon_ymax", dungeon_ymax);
196 settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base);
197 settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
198 settings->setNoiseParams("mgv6_np_steepness", np_steepness);
199 settings->setNoiseParams("mgv6_np_height_select", np_height_select);
200 settings->setNoiseParams("mgv6_np_mud", np_mud);
201 settings->setNoiseParams("mgv6_np_beach", np_beach);
202 settings->setNoiseParams("mgv6_np_biome", np_biome);
203 settings->setNoiseParams("mgv6_np_cave", np_cave);
204 settings->setNoiseParams("mgv6_np_humidity", np_humidity);
205 settings->setNoiseParams("mgv6_np_trees", np_trees);
206 settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees);
210 //////////////////////// Some helper functions for the map generator
212 // Returns Y one under area minimum if not found
213 s16 MapgenV6::find_stone_level(v2s16 p2d)
215 const v3s16 &em = vm->m_area.getExtent();
216 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
217 s16 y_nodes_min = vm->m_area.MinEdge.Y;
218 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
221 for (y = y_nodes_max; y >= y_nodes_min; y--) {
222 content_t c = vm->m_data[i].getContent();
223 if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
226 VoxelArea::add_y(em, i, -1);
228 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
232 // Required by mapgen.h
233 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
235 /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
236 seed, v2s16(blockpos.X, blockpos.Z));*/
237 // Nah, this is just a heuristic, just return something
238 s16 minimum_groundlevel = water_level;
240 if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
247 //////////////////////// Base terrain height functions
249 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
250 float steepness, float height_select)
252 float base = 1 + terrain_base;
253 float higher = 1 + terrain_higher;
255 // Limit higher ground level to at least base
259 // Steepness factor of cliffs
261 b = rangelim(b, 0.0, 1000.0);
262 b = 5 * b * b * b * b * b * b * b;
263 b = rangelim(b, 0.5, 1000.0);
265 // Values 1.5...100 give quite horrible looking slopes
266 if (b > 1.5 && b < 100.0)
267 b = (b < 10.0) ? 1.5 : 100.0;
269 float a_off = -0.20; // Offset to more low
270 float a = 0.5 + b * (a_off + height_select);
271 a = rangelim(a, 0.0, 1.0); // Limit
273 return base * (1.0 - a) + higher * a;
277 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
279 if (spflags & MGV6_FLAT)
282 float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
283 p.X, 0.5, p.Y, 0.5, seed);
284 float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
285 p.X, 0.5, p.Y, 0.5, seed);
286 float steepness = NoisePerlin2D_PO(&noise_steepness->np,
287 p.X, 0.5, p.Y, 0.5, seed);
288 float height_select = NoisePerlin2D_PO(&noise_height_select->np,
289 p.X, 0.5, p.Y, 0.5, seed);
291 return baseTerrainLevel(terrain_base, terrain_higher,
292 steepness, height_select);
296 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
298 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
299 return baseTerrainLevelFromMap(index);
303 float MapgenV6::baseTerrainLevelFromMap(int index)
305 if (spflags & MGV6_FLAT)
308 float terrain_base = noise_terrain_base->result[index];
309 float terrain_higher = noise_terrain_higher->result[index];
310 float steepness = noise_steepness->result[index];
311 float height_select = noise_height_select->result[index];
313 return baseTerrainLevel(terrain_base, terrain_higher,
314 steepness, height_select);
318 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
320 return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
324 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
326 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
330 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
332 s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
333 if (level_at_point <= water_level ||
334 level_at_point > water_level + 16)
335 return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
337 return level_at_point;
341 //////////////////////// Noise functions
343 float MapgenV6::getMudAmount(v2s16 p)
345 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
346 return getMudAmount(index);
350 bool MapgenV6::getHaveBeach(v2s16 p)
352 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
353 return getHaveBeach(index);
357 BiomeV6Type MapgenV6::getBiome(v2s16 p)
359 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
360 + (p.X - full_node_min.X);
361 return getBiome(index, p);
365 float MapgenV6::getHumidity(v2s16 p)
367 /*double noise = noise2d_perlin(
368 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
369 seed+72384, 4, 0.66);
370 noise = (noise + 1.0)/2.0;*/
372 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
373 + (p.X - full_node_min.X);
374 float noise = noise_humidity->result[index];
384 float MapgenV6::getTreeAmount(v2s16 p)
386 /*double noise = noise2d_perlin(
387 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
390 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
391 float zeroval = -0.39;
395 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
399 bool MapgenV6::getHaveAppleTree(v2s16 p)
401 /*is_apple_tree = noise2d_perlin(
402 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
403 data->seed+342902, 3, 0.45) > 0.2;*/
405 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
411 float MapgenV6::getMudAmount(int index)
413 if (spflags & MGV6_FLAT)
414 return MGV6_AVERAGE_MUD_AMOUNT;
416 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
417 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
418 seed+91013, 3, 0.55));*/
420 return noise_mud->result[index];
424 bool MapgenV6::getHaveBeach(int index)
426 // Determine whether to have sand here
427 /*double sandnoise = noise2d_perlin(
428 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
429 seed+59420, 3, 0.50);*/
431 float sandnoise = noise_beach->result[index];
432 return (sandnoise > freq_beach);
436 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
438 // Just do something very simple as for now
439 /*double d = noise2d_perlin(
440 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
441 seed+9130, 3, 0.50);*/
443 float d = noise_biome->result[index];
444 float h = noise_humidity->result[index];
446 if (spflags & MGV6_SNOWBIOMES) {
447 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
449 if (d > MGV6_FREQ_HOT + blend) {
450 if (h > MGV6_FREQ_JUNGLE + blend)
456 if (d < MGV6_FREQ_SNOW + blend) {
457 if (h > MGV6_FREQ_TAIGA + blend)
469 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
470 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
473 if ((spflags & MGV6_JUNGLES) && h > 0.75)
481 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
483 s32 x = p.X, y = p.Y, z = p.Z;
484 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
488 //////////////////////// Map generator
490 void MapgenV6::makeChunk(BlockMakeData *data)
493 assert(data->vmanip);
494 assert(data->nodedef);
495 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
496 data->blockpos_requested.Y >= data->blockpos_min.Y &&
497 data->blockpos_requested.Z >= data->blockpos_min.Z);
498 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
499 data->blockpos_requested.Y <= data->blockpos_max.Y &&
500 data->blockpos_requested.Z <= data->blockpos_max.Z);
502 this->generating = true;
503 this->vm = data->vmanip;
504 this->ndef = data->nodedef;
506 // Hack: use minimum block coords for old code that assumes a single block
507 v3s16 blockpos_min = data->blockpos_min;
508 v3s16 blockpos_max = data->blockpos_max;
510 // Area of central chunk
511 node_min = blockpos_min * MAP_BLOCKSIZE;
512 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
514 // Full allocated area
515 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
516 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
518 central_area_size = node_max - node_min + v3s16(1, 1, 1);
519 assert(central_area_size.X == central_area_size.Z);
521 // Create a block-specific seed
522 blockseed = get_blockseed(data->seed, full_node_min);
527 // Maximum height of the stone surface and obstacles.
528 // This is used to guide the cave generation
529 s16 stone_surface_max_y;
531 // Generate general ground level to full area
532 stone_surface_max_y = generateGround();
534 // Create initial heightmap to limit caves
535 updateHeightmap(node_min, node_max);
537 const s16 max_spread_amount = MAP_BLOCKSIZE;
538 // Limit dirt flow area by 1 because mud is flown into neighbors.
539 s16 mudflow_minpos = -max_spread_amount + 1;
540 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
542 // Loop this part, it will make stuff look older and newer nicely
543 const u32 age_loops = 2;
544 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
545 // Make caves (this code is relatively horrible)
546 if (flags & MG_CAVES)
547 generateCaves(stone_surface_max_y);
549 // Add mud to the central chunk
552 // Flow mud away from steep edges
553 if (spflags & MGV6_MUDFLOW)
554 flowMud(mudflow_minpos, mudflow_maxpos);
558 // Update heightmap after mudflow
559 updateHeightmap(node_min, node_max);
562 if ((flags & MG_DUNGEONS) && stone_surface_max_y >= node_min.Y &&
563 full_node_min.Y >= dungeon_ymin && full_node_max.Y <= dungeon_ymax) {
568 dp.only_in_ground = true;
569 dp.corridor_len_min = 1;
570 dp.corridor_len_max = 13;
573 dp.y_min = -MAX_MAP_GENERATION_LIMIT;
574 dp.y_max = MAX_MAP_GENERATION_LIMIT;
577 = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
579 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
581 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
582 dp.c_wall = c_desert_stone;
583 dp.c_alt_wall = CONTENT_IGNORE;
584 dp.c_stair = c_stair_desert_stone;
586 dp.diagonal_dirs = true;
587 dp.holesize = v3s16(2, 3, 2);
588 dp.room_size_min = v3s16(6, 9, 6);
589 dp.room_size_max = v3s16(10, 11, 10);
590 dp.room_size_large_min = v3s16(10, 13, 10);
591 dp.room_size_large_max = v3s16(18, 21, 18);
592 dp.notifytype = GENNOTIFY_TEMPLE;
594 dp.c_wall = c_cobble;
595 dp.c_alt_wall = c_mossycobble;
596 dp.c_stair = c_stair_cobble;
598 dp.diagonal_dirs = false;
599 dp.holesize = v3s16(1, 2, 1);
600 dp.room_size_min = v3s16(4, 4, 4);
601 dp.room_size_max = v3s16(8, 6, 8);
602 dp.room_size_large_min = v3s16(8, 8, 8);
603 dp.room_size_large_max = v3s16(16, 16, 16);
604 dp.notifytype = GENNOTIFY_DUNGEON;
607 DungeonGen dgen(ndef, &gennotify, &dp);
608 dgen.generate(vm, blockseed, full_node_min, full_node_max);
611 // Add top and bottom side of water to transforming_liquid queue
612 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
617 // Generate some trees, and add grass, if a jungle
618 if (spflags & MGV6_TREES)
619 placeTreesAndJungleGrass();
621 // Generate the registered decorations
622 if (flags & MG_DECORATIONS)
623 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
625 // Generate the registered ores
626 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
628 // Calculate lighting
629 if (flags & MG_LIGHT)
630 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
631 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
632 full_node_min, full_node_max);
634 this->generating = false;
638 void MapgenV6::calculateNoise()
642 int fx = full_node_min.X;
643 int fz = full_node_min.Z;
645 if (!(spflags & MGV6_FLAT)) {
646 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
647 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
648 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
649 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
650 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
653 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
655 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
656 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
657 // Humidity map does not need range limiting 0 to 1,
658 // only humidity at point does
662 int MapgenV6::generateGround()
664 //TimeTaker timer1("Generating ground level");
665 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
666 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
667 MapNode n_ice(c_ice);
668 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
671 for (s16 z = node_min.Z; z <= node_max.Z; z++)
672 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
674 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
677 if (surface_y > stone_surface_max_y)
678 stone_surface_max_y = surface_y;
680 BiomeV6Type bt = getBiome(v2s16(x, z));
682 // Fill ground with stone
683 const v3s16 &em = vm->m_area.getExtent();
684 u32 i = vm->m_area.index(x, node_min.Y, z);
685 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
686 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
687 if (y <= surface_y) {
688 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
689 && bt == BT_DESERT) ?
690 n_desert_stone : n_stone;
691 } else if (y <= water_level) {
692 vm->m_data[i] = (y >= MGV6_ICE_BASE
693 && bt == BT_TUNDRA) ?
694 n_ice : n_water_source;
696 vm->m_data[i] = n_air;
699 VoxelArea::add_y(em, i, 1);
703 return stone_surface_max_y;
707 void MapgenV6::addMud()
710 //TimeTaker timer1("add mud");
711 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
712 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
716 for (s16 z = node_min.Z; z <= node_max.Z; z++)
717 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
718 // Randomize mud amount
719 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
722 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
724 // Handle area not found
725 if (surface_y == vm->m_area.MinEdge.Y - 1)
728 BiomeV6Type bt = getBiome(v2s16(x, z));
729 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
731 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
733 } else if (mud_add_amount <= 0) {
734 mud_add_amount = 1 - mud_add_amount;
736 } else if (bt != BT_DESERT && getHaveBeach(index) &&
737 surface_y + mud_add_amount <= water_level + 2) {
741 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
742 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
744 /* If topmost node is grass, change it to mud. It might be if it was
745 // flown to there from a neighboring chunk and then converted.
746 u32 i = vm->m_area.index(x, surface_y, z);
747 if (vm->m_data[i].getContent() == c_dirt_with_grass)
748 vm->m_data[i] = n_dirt;*/
752 const v3s16 &em = vm->m_area.getExtent();
753 s16 y_start = surface_y + 1;
754 u32 i = vm->m_area.index(x, y_start, z);
755 for (s16 y = y_start; y <= node_max.Y; y++) {
756 if (mudcount >= mud_add_amount)
759 vm->m_data[i] = addnode;
762 VoxelArea::add_y(em, i, 1);
768 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
771 //TimeTaker timer1("flow mud");
773 // Iterate a few times
774 for (s16 k = 0; k < 3; k++) {
775 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
776 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
777 // Invert coordinates every 2nd iteration
779 x = mudflow_maxpos - (x - mudflow_minpos);
780 z = mudflow_maxpos - (z - mudflow_minpos);
783 // Node position in 2d
784 v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
786 const v3s16 &em = vm->m_area.getExtent();
787 u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
790 while (y >= node_min.Y) {
795 for (; y >= node_min.Y; y--) {
797 if (n->getContent() == c_dirt ||
798 n->getContent() == c_dirt_with_grass ||
799 n->getContent() == c_gravel)
802 VoxelArea::add_y(em, i, -1);
805 // Stop if out of area
806 //if(vmanip.m_area.contains(i) == false)
810 if (n->getContent() == c_dirt ||
811 n->getContent() == c_dirt_with_grass) {
812 // Make it exactly mud
813 n->setContent(c_dirt);
815 // Don't flow it if the stuff under it is not mud
818 VoxelArea::add_y(em, i2, -1);
819 // Cancel if out of area
820 if (!vm->m_area.contains(i2))
822 MapNode *n2 = &vm->m_data[i2];
823 if (n2->getContent() != c_dirt &&
824 n2->getContent() != c_dirt_with_grass)
829 static const v3s16 dirs4[4] = {
830 v3s16(0, 0, 1), // back
831 v3s16(1, 0, 0), // right
832 v3s16(0, 0, -1), // front
833 v3s16(-1, 0, 0), // left
836 // Check that upper is walkable. Cancel
837 // dropping if upper keeps it in place.
839 VoxelArea::add_y(em, i3, 1);
842 if (vm->m_area.contains(i3)) {
843 n3 = &vm->m_data[i3];
844 if (ndef->get(*n3).walkable)
849 for (const v3s16 &dirp : dirs4) {
852 VoxelArea::add_p(em, i2, dirp);
853 // Fail if out of area
854 if (!vm->m_area.contains(i2))
856 // Check that side is air
857 MapNode *n2 = &vm->m_data[i2];
858 if (ndef->get(*n2).walkable)
860 // Check that under side is air
861 VoxelArea::add_y(em, i2, -1);
862 if (!vm->m_area.contains(i2))
864 n2 = &vm->m_data[i2];
865 if (ndef->get(*n2).walkable)
867 // Loop further down until not air
868 bool dropped_to_unknown = false;
870 VoxelArea::add_y(em, i2, -1);
871 n2 = &vm->m_data[i2];
872 // if out of known area
873 if (!vm->m_area.contains(i2) ||
874 n2->getContent() == CONTENT_IGNORE) {
875 dropped_to_unknown = true;
878 } while (!ndef->get(*n2).walkable);
879 // Loop one up so that we're in air
880 VoxelArea::add_y(em, i2, 1);
882 // Move mud to new place. Outside mapchunk remove
883 // any decorations above removed or placed mud.
884 if (!dropped_to_unknown)
885 moveMud(i, i2, i3, p2d, em);
897 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
898 u32 above_remove_index, v2s16 pos, v3s16 em)
900 MapNode n_air(CONTENT_AIR);
901 // Copy mud from old place to new place
902 vm->m_data[place_index] = vm->m_data[remove_index];
903 // Set old place to be air
904 vm->m_data[remove_index] = n_air;
905 // Outside the mapchunk decorations may need to be removed if above removed
906 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
907 // use 'pos.X >= node_max.X' etc.
908 if (pos.X >= node_max.X || pos.X <= node_min.X ||
909 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
910 // 'above remove' node is above removed mud. If it is not air, water or
911 // 'ignore' it is a decoration that needs removing. Also search upwards
912 // to remove a possible stacked decoration.
913 // Check for 'ignore' because stacked decorations can penetrate into
914 // 'ignore' nodes above the mapchunk.
915 while (vm->m_area.contains(above_remove_index) &&
916 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
917 vm->m_data[above_remove_index].getContent() != c_water_source &&
918 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
919 vm->m_data[above_remove_index] = n_air;
920 VoxelArea::add_y(em, above_remove_index, 1);
922 // Mud placed may have partially-buried a stacked decoration, search
924 VoxelArea::add_y(em, place_index, 1);
925 while (vm->m_area.contains(place_index) &&
926 vm->m_data[place_index].getContent() != CONTENT_AIR &&
927 vm->m_data[place_index].getContent() != c_water_source &&
928 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
929 vm->m_data[place_index] = n_air;
930 VoxelArea::add_y(em, place_index, 1);
936 void MapgenV6::placeTreesAndJungleGrass()
938 //TimeTaker t("placeTrees");
939 if (node_max.Y < water_level)
942 PseudoRandom grassrandom(blockseed + 53);
943 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
944 // if we don't have junglegrass, don't place cignore... that's bad
945 if (c_junglegrass == CONTENT_IGNORE)
946 c_junglegrass = CONTENT_AIR;
947 MapNode n_junglegrass(c_junglegrass);
948 const v3s16 &em = vm->m_area.getExtent();
950 // Divide area into parts
952 s16 sidelen = central_area_size.X / div;
953 double area = sidelen * sidelen;
955 // N.B. We must add jungle grass first, since tree leaves will
956 // obstruct the ground, giving us a false ground level
957 for (s16 z0 = 0; z0 < div; z0++)
958 for (s16 x0 = 0; x0 < div; x0++) {
959 // Center position of part of division
961 node_min.X + sidelen / 2 + sidelen * x0,
962 node_min.Z + sidelen / 2 + sidelen * z0
964 // Minimum edge of part of division
966 node_min.X + sidelen * x0,
967 node_min.Z + sidelen * z0
969 // Maximum edge of part of division
971 node_min.X + sidelen + sidelen * x0 - 1,
972 node_min.Z + sidelen + sidelen * z0 - 1
975 // Get biome at center position of part of division
976 BiomeV6Type bt = getBiome(p2d_center);
980 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
981 tree_count = area * getTreeAmount(p2d_center);
989 if (bt == BT_JUNGLE) {
990 float humidity = getHumidity(p2d_center);
991 u32 grass_count = 5 * humidity * tree_count;
992 for (u32 i = 0; i < grass_count; i++) {
993 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
994 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
995 int mapindex = central_area_size.X * (z - node_min.Z)
997 s16 y = heightmap[mapindex];
1001 u32 vi = vm->m_area.index(x, y, z);
1002 // place on dirt_with_grass, since we know it is exposed to sunlight
1003 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1004 VoxelArea::add_y(em, vi, 1);
1005 vm->m_data[vi] = n_junglegrass;
1010 // Put trees in random places on part of division
1011 for (u32 i = 0; i < tree_count; i++) {
1012 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1013 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1014 int mapindex = central_area_size.X * (z - node_min.Z)
1016 s16 y = heightmap[mapindex];
1017 // Don't make a tree under water level
1018 // Don't make a tree so high that it doesn't fit
1019 if (y < water_level || y > node_max.Y - 6)
1023 // Trees grow only on mud and grass
1025 u32 i = vm->m_area.index(p);
1026 content_t c = vm->m_data[i].getContent();
1028 c != c_dirt_with_grass &&
1029 c != c_dirt_with_snow)
1035 if (bt == BT_JUNGLE) {
1036 treegen::make_jungletree(*vm, p, ndef, myrand());
1037 } else if (bt == BT_TAIGA) {
1038 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1039 } else if (bt == BT_NORMAL) {
1040 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1041 getHaveAppleTree(v2s16(x, z));
1042 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1046 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1050 void MapgenV6::growGrass() // Add surface nodes
1052 MapNode n_dirt_with_grass(c_dirt_with_grass);
1053 MapNode n_dirt_with_snow(c_dirt_with_snow);
1054 MapNode n_snowblock(c_snowblock);
1055 MapNode n_snow(c_snow);
1056 const v3s16 &em = vm->m_area.getExtent();
1059 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1060 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1061 // Find the lowest surface to which enough light ends up to make
1062 // grass grow. Basically just wait until not air and not leaves.
1065 u32 i = vm->m_area.index(x, node_max.Y, z);
1067 // Go to ground level
1068 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1069 MapNode &n = vm->m_data[i];
1070 if (ndef->get(n).param_type != CPT_LIGHT ||
1071 ndef->get(n).liquid_type != LIQUID_NONE ||
1072 n.getContent() == c_ice)
1074 VoxelArea::add_y(em, i, -1);
1076 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1079 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1080 u32 i = vm->m_area.index(x, surface_y, z);
1081 content_t c = vm->m_data[i].getContent();
1082 if (surface_y >= water_level - 20) {
1083 if (bt == BT_TAIGA && c == c_dirt) {
1084 vm->m_data[i] = n_dirt_with_snow;
1085 } else if (bt == BT_TUNDRA) {
1087 vm->m_data[i] = n_snowblock;
1088 VoxelArea::add_y(em, i, -1);
1089 vm->m_data[i] = n_dirt_with_snow;
1090 } else if (c == c_stone && surface_y < node_max.Y) {
1091 VoxelArea::add_y(em, i, 1);
1092 vm->m_data[i] = n_snowblock;
1094 } else if (c == c_dirt) {
1095 vm->m_data[i] = n_dirt_with_grass;
1102 void MapgenV6::generateCaves(int max_stone_y)
1104 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1105 int volume_nodes = (node_max.X - node_min.X + 1) *
1106 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1107 cave_amount = MYMAX(0.0, cave_amount);
1108 u32 caves_count = cave_amount * volume_nodes / 50000;
1109 u32 bruises_count = 1;
1110 PseudoRandom ps(blockseed + 21343);
1111 PseudoRandom ps2(blockseed + 1032);
1113 if (ps.range(1, 6) == 1)
1114 bruises_count = ps.range(0, ps.range(0, 2));
1116 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1121 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1122 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1124 bool large_cave = (i >= caves_count);
1125 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1126 large_cave, max_stone_y, heightmap);