3 Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 Copyright (C) 2014-2017 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;
70 np_cave = ¶ms->np_cave;
71 np_humidity = ¶ms->np_humidity;
72 np_trees = ¶ms->np_trees;
73 np_apple_trees = ¶ms->np_apple_trees;
75 //// Create noise objects
76 noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Y);
77 noise_terrain_higher = new Noise(¶ms->np_terrain_higher, seed, csize.X, csize.Y);
78 noise_steepness = new Noise(¶ms->np_steepness, seed, csize.X, csize.Y);
79 noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Y);
80 noise_mud = new Noise(¶ms->np_mud, seed, csize.X, csize.Y);
81 noise_beach = new Noise(¶ms->np_beach, seed, csize.X, csize.Y);
82 noise_biome = new Noise(¶ms->np_biome, seed,
83 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
84 noise_humidity = new Noise(¶ms->np_humidity, seed,
85 csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
87 //// Resolve nodes to be used
88 INodeDefManager *ndef = emerge->ndef;
90 c_stone = ndef->getId("mapgen_stone");
91 c_dirt = ndef->getId("mapgen_dirt");
92 c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
93 c_sand = ndef->getId("mapgen_sand");
94 c_water_source = ndef->getId("mapgen_water_source");
95 c_lava_source = ndef->getId("mapgen_lava_source");
96 c_gravel = ndef->getId("mapgen_gravel");
97 c_desert_stone = ndef->getId("mapgen_desert_stone");
98 c_desert_sand = ndef->getId("mapgen_desert_sand");
99 c_dirt_with_snow = ndef->getId("mapgen_dirt_with_snow");
100 c_snow = ndef->getId("mapgen_snow");
101 c_snowblock = ndef->getId("mapgen_snowblock");
102 c_ice = ndef->getId("mapgen_ice");
104 if (c_gravel == CONTENT_IGNORE)
106 if (c_desert_stone == CONTENT_IGNORE)
107 c_desert_stone = c_stone;
108 if (c_desert_sand == CONTENT_IGNORE)
109 c_desert_sand = c_sand;
110 if (c_dirt_with_snow == CONTENT_IGNORE)
111 c_dirt_with_snow = c_dirt_with_grass;
112 if (c_snow == CONTENT_IGNORE)
113 c_snow = CONTENT_AIR;
114 if (c_snowblock == CONTENT_IGNORE)
115 c_snowblock = c_dirt_with_grass;
116 if (c_ice == CONTENT_IGNORE)
117 c_ice = c_water_source;
119 c_cobble = ndef->getId("mapgen_cobble");
120 c_mossycobble = ndef->getId("mapgen_mossycobble");
121 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
122 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
124 if (c_mossycobble == CONTENT_IGNORE)
125 c_mossycobble = c_cobble;
126 if (c_stair_cobble == CONTENT_IGNORE)
127 c_stair_cobble = c_cobble;
128 if (c_stair_desert_stone == CONTENT_IGNORE)
129 c_stair_desert_stone = c_desert_stone;
133 MapgenV6::~MapgenV6()
135 delete noise_terrain_base;
136 delete noise_terrain_higher;
137 delete noise_steepness;
138 delete noise_height_select;
142 delete noise_humidity;
148 MapgenV6Params::MapgenV6Params()
150 np_terrain_base = NoiseParams(-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0);
151 np_terrain_higher = NoiseParams(20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0);
152 np_steepness = NoiseParams(0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0);
153 np_height_select = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0);
154 np_mud = NoiseParams(4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0);
155 np_beach = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0);
156 np_biome = NoiseParams(0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0);
157 np_cave = NoiseParams(6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0);
158 np_humidity = NoiseParams(0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0);
159 np_trees = NoiseParams(0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0);
160 np_apple_trees = NoiseParams(0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0);
164 void MapgenV6Params::readParams(const Settings *settings)
166 settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
167 settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
168 settings->getFloatNoEx("mgv6_freq_beach", freq_beach);
170 settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base);
171 settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
172 settings->getNoiseParams("mgv6_np_steepness", np_steepness);
173 settings->getNoiseParams("mgv6_np_height_select", np_height_select);
174 settings->getNoiseParams("mgv6_np_mud", np_mud);
175 settings->getNoiseParams("mgv6_np_beach", np_beach);
176 settings->getNoiseParams("mgv6_np_biome", np_biome);
177 settings->getNoiseParams("mgv6_np_cave", np_cave);
178 settings->getNoiseParams("mgv6_np_humidity", np_humidity);
179 settings->getNoiseParams("mgv6_np_trees", np_trees);
180 settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees);
184 void MapgenV6Params::writeParams(Settings *settings) const
186 settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
187 settings->setFloat("mgv6_freq_desert", freq_desert);
188 settings->setFloat("mgv6_freq_beach", freq_beach);
190 settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base);
191 settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
192 settings->setNoiseParams("mgv6_np_steepness", np_steepness);
193 settings->setNoiseParams("mgv6_np_height_select", np_height_select);
194 settings->setNoiseParams("mgv6_np_mud", np_mud);
195 settings->setNoiseParams("mgv6_np_beach", np_beach);
196 settings->setNoiseParams("mgv6_np_biome", np_biome);
197 settings->setNoiseParams("mgv6_np_cave", np_cave);
198 settings->setNoiseParams("mgv6_np_humidity", np_humidity);
199 settings->setNoiseParams("mgv6_np_trees", np_trees);
200 settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees);
204 //////////////////////// Some helper functions for the map generator
206 // Returns Y one under area minimum if not found
207 s16 MapgenV6::find_stone_level(v2s16 p2d)
209 const v3s16 &em = vm->m_area.getExtent();
210 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
211 s16 y_nodes_min = vm->m_area.MinEdge.Y;
212 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
215 for (y = y_nodes_max; y >= y_nodes_min; y--) {
216 content_t c = vm->m_data[i].getContent();
217 if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
220 vm->m_area.add_y(em, i, -1);
222 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
226 // Required by mapgen.h
227 bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
229 /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
230 seed, v2s16(blockpos.X, blockpos.Z));*/
231 // Nah, this is just a heuristic, just return something
232 s16 minimum_groundlevel = water_level;
234 if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
241 //////////////////////// Base terrain height functions
243 float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
244 float steepness, float height_select)
246 float base = 1 + terrain_base;
247 float higher = 1 + terrain_higher;
249 // Limit higher ground level to at least base
253 // Steepness factor of cliffs
255 b = rangelim(b, 0.0, 1000.0);
256 b = 5 * b * b * b * b * b * b * b;
257 b = rangelim(b, 0.5, 1000.0);
259 // Values 1.5...100 give quite horrible looking slopes
260 if (b > 1.5 && b < 100.0)
261 b = (b < 10.0) ? 1.5 : 100.0;
263 float a_off = -0.20; // Offset to more low
264 float a = 0.5 + b * (a_off + height_select);
265 a = rangelim(a, 0.0, 1.0); // Limit
267 return base * (1.0 - a) + higher * a;
271 float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
273 if (spflags & MGV6_FLAT)
276 float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np,
277 p.X, 0.5, p.Y, 0.5, seed);
278 float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
279 p.X, 0.5, p.Y, 0.5, seed);
280 float steepness = NoisePerlin2D_PO(&noise_steepness->np,
281 p.X, 0.5, p.Y, 0.5, seed);
282 float height_select = NoisePerlin2D_PO(&noise_height_select->np,
283 p.X, 0.5, p.Y, 0.5, seed);
285 return baseTerrainLevel(terrain_base, terrain_higher,
286 steepness, height_select);
290 float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
292 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
293 return baseTerrainLevelFromMap(index);
297 float MapgenV6::baseTerrainLevelFromMap(int index)
299 if (spflags & MGV6_FLAT)
302 float terrain_base = noise_terrain_base->result[index];
303 float terrain_higher = noise_terrain_higher->result[index];
304 float steepness = noise_steepness->result[index];
305 float height_select = noise_height_select->result[index];
307 return baseTerrainLevel(terrain_base, terrain_higher,
308 steepness, height_select);
312 s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
314 return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
318 int MapgenV6::getGroundLevelAtPoint(v2s16 p)
320 return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
324 int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
326 s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
327 if (level_at_point <= water_level ||
328 level_at_point > water_level + 16)
329 return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
331 return level_at_point;
335 //////////////////////// Noise functions
337 float MapgenV6::getMudAmount(v2s16 p)
339 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
340 return getMudAmount(index);
344 bool MapgenV6::getHaveBeach(v2s16 p)
346 int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
347 return getHaveBeach(index);
351 BiomeV6Type MapgenV6::getBiome(v2s16 p)
353 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
354 + (p.X - full_node_min.X);
355 return getBiome(index, p);
359 float MapgenV6::getHumidity(v2s16 p)
361 /*double noise = noise2d_perlin(
362 0.5+(float)p.X/500, 0.5+(float)p.Y/500,
363 seed+72384, 4, 0.66);
364 noise = (noise + 1.0)/2.0;*/
366 int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
367 + (p.X - full_node_min.X);
368 float noise = noise_humidity->result[index];
378 float MapgenV6::getTreeAmount(v2s16 p)
380 /*double noise = noise2d_perlin(
381 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
384 float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
385 float zeroval = -0.39;
389 return 0.04 * (noise - zeroval) / (1.0 - zeroval);
393 bool MapgenV6::getHaveAppleTree(v2s16 p)
395 /*is_apple_tree = noise2d_perlin(
396 0.5+(float)p.X/100, 0.5+(float)p.Z/100,
397 data->seed+342902, 3, 0.45) > 0.2;*/
399 float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
405 float MapgenV6::getMudAmount(int index)
407 if (spflags & MGV6_FLAT)
408 return MGV6_AVERAGE_MUD_AMOUNT;
410 /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
411 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
412 seed+91013, 3, 0.55));*/
414 return noise_mud->result[index];
418 bool MapgenV6::getHaveBeach(int index)
420 // Determine whether to have sand here
421 /*double sandnoise = noise2d_perlin(
422 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
423 seed+59420, 3, 0.50);*/
425 float sandnoise = noise_beach->result[index];
426 return (sandnoise > freq_beach);
430 BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
432 // Just do something very simple as for now
433 /*double d = noise2d_perlin(
434 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
435 seed+9130, 3, 0.50);*/
437 float d = noise_biome->result[index];
438 float h = noise_humidity->result[index];
440 if (spflags & MGV6_SNOWBIOMES) {
441 float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
443 if (d > MGV6_FREQ_HOT + blend) {
444 if (h > MGV6_FREQ_JUNGLE + blend)
448 } else if (d < MGV6_FREQ_SNOW + blend) {
449 if (h > MGV6_FREQ_TAIGA + blend)
460 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
461 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
464 if ((spflags & MGV6_JUNGLES) && h > 0.75)
472 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
474 s32 x = p.X, y = p.Y, z = p.Z;
475 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
479 //////////////////////// Map generator
481 void MapgenV6::makeChunk(BlockMakeData *data)
484 assert(data->vmanip);
485 assert(data->nodedef);
486 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
487 data->blockpos_requested.Y >= data->blockpos_min.Y &&
488 data->blockpos_requested.Z >= data->blockpos_min.Z);
489 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
490 data->blockpos_requested.Y <= data->blockpos_max.Y &&
491 data->blockpos_requested.Z <= data->blockpos_max.Z);
493 this->generating = true;
494 this->vm = data->vmanip;
495 this->ndef = data->nodedef;
497 // Hack: use minimum block coords for old code that assumes a single block
498 v3s16 blockpos_min = data->blockpos_min;
499 v3s16 blockpos_max = data->blockpos_max;
501 // Area of central chunk
502 node_min = blockpos_min * MAP_BLOCKSIZE;
503 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
505 // Full allocated area
506 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
507 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
509 central_area_size = node_max - node_min + v3s16(1, 1, 1);
510 assert(central_area_size.X == central_area_size.Z);
512 int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
513 * (blockpos_max.Y - blockpos_min.Y + 1)
514 * (blockpos_max.Z - blockpos_max.Z + 1);
516 volume_nodes = volume_blocks *
517 MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
519 // Create a block-specific seed
520 blockseed = get_blockseed(data->seed, full_node_min);
525 // Maximum height of the stone surface and obstacles.
526 // This is used to guide the cave generation
527 s16 stone_surface_max_y;
529 // Generate general ground level to full area
530 stone_surface_max_y = generateGround();
532 // Create initial heightmap to limit caves
533 updateHeightmap(node_min, node_max);
535 const s16 max_spread_amount = MAP_BLOCKSIZE;
536 // Limit dirt flow area by 1 because mud is flown into neighbors.
537 s16 mudflow_minpos = -max_spread_amount + 1;
538 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
540 // Loop this part, it will make stuff look older and newer nicely
541 const u32 age_loops = 2;
542 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
543 // Make caves (this code is relatively horrible)
544 if (flags & MG_CAVES)
545 generateCaves(stone_surface_max_y);
547 // Add mud to the central chunk
550 // Flow mud away from steep edges
551 if (spflags & MGV6_MUDFLOW)
552 flowMud(mudflow_minpos, mudflow_maxpos);
556 // Update heightmap after mudflow
557 updateHeightmap(node_min, node_max);
560 if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
564 dp.c_water = c_water_source;
565 dp.c_river_water = c_water_source;
567 dp.only_in_ground = true;
568 dp.corridor_len_min = 1;
569 dp.corridor_len_max = 13;
572 dp.y_min = -MAX_MAP_GENERATION_LIMIT;
573 dp.y_max = MAX_MAP_GENERATION_LIMIT;
576 = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
578 = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
580 if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
581 dp.c_wall = c_desert_stone;
582 dp.c_alt_wall = CONTENT_IGNORE;
583 dp.c_stair = c_stair_desert_stone;
585 dp.diagonal_dirs = true;
586 dp.holesize = v3s16(2, 3, 2);
587 dp.room_size_min = v3s16(6, 9, 6);
588 dp.room_size_max = v3s16(10, 11, 10);
589 dp.room_size_large_min = v3s16(10, 13, 10);
590 dp.room_size_large_max = v3s16(18, 21, 18);
591 dp.notifytype = GENNOTIFY_TEMPLE;
593 dp.c_wall = c_cobble;
594 dp.c_alt_wall = c_mossycobble;
595 dp.c_stair = c_stair_cobble;
597 dp.diagonal_dirs = false;
598 dp.holesize = v3s16(1, 2, 1);
599 dp.room_size_min = v3s16(4, 4, 4);
600 dp.room_size_max = v3s16(8, 6, 8);
601 dp.room_size_large_min = v3s16(8, 8, 8);
602 dp.room_size_large_max = v3s16(16, 16, 16);
603 dp.notifytype = GENNOTIFY_DUNGEON;
606 DungeonGen dgen(ndef, &gennotify, &dp);
607 dgen.generate(vm, blockseed, full_node_min, full_node_max);
610 // Add top and bottom side of water to transforming_liquid queue
611 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
616 // Generate some trees, and add grass, if a jungle
617 if (spflags & MGV6_TREES)
618 placeTreesAndJungleGrass();
620 // Generate the registered decorations
621 if (flags & MG_DECORATIONS)
622 m_emerge->decomgr->placeAllDecos(this, blockseed,
623 node_min, node_max, water_level - 1);
625 // Generate the registered ores
626 m_emerge->oremgr->placeAllOres(this, blockseed,
627 node_min, node_max, water_level - 1);
629 // Calculate lighting
630 if (flags & MG_LIGHT)
631 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
632 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
633 full_node_min, full_node_max);
635 this->generating = false;
639 void MapgenV6::calculateNoise()
643 int fx = full_node_min.X;
644 int fz = full_node_min.Z;
646 if (!(spflags & MGV6_FLAT)) {
647 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
648 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
649 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
650 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
651 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
654 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
656 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
657 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
658 // Humidity map does not need range limiting 0 to 1,
659 // only humidity at point does
663 int MapgenV6::generateGround()
665 //TimeTaker timer1("Generating ground level");
666 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
667 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
668 MapNode n_ice(c_ice);
669 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
672 for (s16 z = node_min.Z; z <= node_max.Z; z++)
673 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
675 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
678 if (surface_y > stone_surface_max_y)
679 stone_surface_max_y = surface_y;
681 BiomeV6Type bt = getBiome(v2s16(x, z));
683 // Fill ground with stone
684 const v3s16 &em = vm->m_area.getExtent();
685 u32 i = vm->m_area.index(x, node_min.Y, z);
686 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
687 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
688 if (y <= surface_y) {
689 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
690 && bt == BT_DESERT) ?
691 n_desert_stone : n_stone;
692 } else if (y <= water_level) {
693 vm->m_data[i] = (y >= MGV6_ICE_BASE
694 && bt == BT_TUNDRA) ?
695 n_ice : n_water_source;
697 vm->m_data[i] = n_air;
700 vm->m_area.add_y(em, i, 1);
704 return stone_surface_max_y;
708 void MapgenV6::addMud()
711 //TimeTaker timer1("add mud");
712 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
713 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
717 for (s16 z = node_min.Z; z <= node_max.Z; z++)
718 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
719 // Randomize mud amount
720 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
723 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
725 // Handle area not found
726 if (surface_y == vm->m_area.MinEdge.Y - 1)
729 BiomeV6Type bt = getBiome(v2s16(x, z));
730 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
732 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
734 } else if (mud_add_amount <= 0) {
735 mud_add_amount = 1 - mud_add_amount;
737 } else if (bt != BT_DESERT && getHaveBeach(index) &&
738 surface_y + mud_add_amount <= water_level + 2) {
742 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
743 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
745 /* If topmost node is grass, change it to mud. It might be if it was
746 // flown to there from a neighboring chunk and then converted.
747 u32 i = vm->m_area.index(x, surface_y, z);
748 if (vm->m_data[i].getContent() == c_dirt_with_grass)
749 vm->m_data[i] = n_dirt;*/
753 const v3s16 &em = vm->m_area.getExtent();
754 s16 y_start = surface_y + 1;
755 u32 i = vm->m_area.index(x, y_start, z);
756 for (s16 y = y_start; y <= node_max.Y; y++) {
757 if (mudcount >= mud_add_amount)
760 vm->m_data[i] = addnode;
763 vm->m_area.add_y(em, i, 1);
769 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
772 //TimeTaker timer1("flow mud");
774 // Iterate a few times
775 for (s16 k = 0; k < 3; k++) {
776 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
777 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
778 // Invert coordinates every 2nd iteration
780 x = mudflow_maxpos - (x - mudflow_minpos);
781 z = mudflow_maxpos - (z - mudflow_minpos);
784 // Node position in 2d
785 v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
787 const v3s16 &em = vm->m_area.getExtent();
788 u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
791 while (y >= node_min.Y) {
796 for (; y >= node_min.Y; y--) {
798 if (n->getContent() == c_dirt ||
799 n->getContent() == c_dirt_with_grass ||
800 n->getContent() == c_gravel)
803 vm->m_area.add_y(em, i, -1);
806 // Stop if out of area
807 //if(vmanip.m_area.contains(i) == false)
811 if (n->getContent() == c_dirt ||
812 n->getContent() == c_dirt_with_grass) {
813 // Make it exactly mud
814 n->setContent(c_dirt);
816 // Don't flow it if the stuff under it is not mud
819 vm->m_area.add_y(em, i2, -1);
820 // Cancel if out of area
821 if (vm->m_area.contains(i2) == false)
823 MapNode *n2 = &vm->m_data[i2];
824 if (n2->getContent() != c_dirt &&
825 n2->getContent() != c_dirt_with_grass)
831 v3s16(0, 0, 1), // back
832 v3s16(1, 0, 0), // right
833 v3s16(0, 0, -1), // front
834 v3s16(-1, 0, 0), // left
837 // Check that upper is walkable. Cancel
838 // dropping if upper keeps it in place.
840 vm->m_area.add_y(em, i3, 1);
843 if (vm->m_area.contains(i3)) {
844 n3 = &vm->m_data[i3];
845 if (ndef->get(*n3).walkable)
850 for (u32 di = 0; di < 4; di++) {
851 v3s16 dirp = dirs4[di];
854 vm->m_area.add_p(em, i2, dirp);
855 // Fail if out of area
856 if (vm->m_area.contains(i2) == false)
858 // Check that side is air
859 MapNode *n2 = &vm->m_data[i2];
860 if (ndef->get(*n2).walkable)
862 // Check that under side is air
863 vm->m_area.add_y(em, i2, -1);
864 if (vm->m_area.contains(i2) == false)
866 n2 = &vm->m_data[i2];
867 if (ndef->get(*n2).walkable)
869 // Loop further down until not air
870 bool dropped_to_unknown = false;
872 vm->m_area.add_y(em, i2, -1);
873 n2 = &vm->m_data[i2];
874 // if out of known area
875 if (vm->m_area.contains(i2) == false ||
876 n2->getContent() == CONTENT_IGNORE) {
877 dropped_to_unknown = true;
880 } while (ndef->get(*n2).walkable == false);
881 // Loop one up so that we're in air
882 vm->m_area.add_y(em, i2, 1);
884 // Move mud to new place. Outside mapchunk remove
885 // any decorations above removed or placed mud.
886 if (!dropped_to_unknown)
887 moveMud(i, i2, i3, p2d, em);
899 void MapgenV6::moveMud(u32 remove_index, u32 place_index,
900 u32 above_remove_index, v2s16 pos, v3s16 em)
902 MapNode n_air(CONTENT_AIR);
903 // Copy mud from old place to new place
904 vm->m_data[place_index] = vm->m_data[remove_index];
905 // Set old place to be air
906 vm->m_data[remove_index] = n_air;
907 // Outside the mapchunk decorations may need to be removed if above removed
908 // mud or if half-buried in placed mud. Placed mud is to the side of pos so
909 // use 'pos.X >= node_max.X' etc.
910 if (pos.X >= node_max.X || pos.X <= node_min.X ||
911 pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
912 // 'above remove' node is above removed mud. If it is not air, water or
913 // 'ignore' it is a decoration that needs removing. Also search upwards
914 // to remove a possible stacked decoration.
915 // Check for 'ignore' because stacked decorations can penetrate into
916 // 'ignore' nodes above the mapchunk.
917 while (vm->m_area.contains(above_remove_index) &&
918 vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
919 vm->m_data[above_remove_index].getContent() != c_water_source &&
920 vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
921 vm->m_data[above_remove_index] = n_air;
922 vm->m_area.add_y(em, above_remove_index, 1);
924 // Mud placed may have partially-buried a stacked decoration, search
926 vm->m_area.add_y(em, place_index, 1);
927 while (vm->m_area.contains(place_index) &&
928 vm->m_data[place_index].getContent() != CONTENT_AIR &&
929 vm->m_data[place_index].getContent() != c_water_source &&
930 vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
931 vm->m_data[place_index] = n_air;
932 vm->m_area.add_y(em, place_index, 1);
938 void MapgenV6::placeTreesAndJungleGrass()
940 //TimeTaker t("placeTrees");
941 if (node_max.Y < water_level)
944 PseudoRandom grassrandom(blockseed + 53);
945 content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
946 // if we don't have junglegrass, don't place cignore... that's bad
947 if (c_junglegrass == CONTENT_IGNORE)
948 c_junglegrass = CONTENT_AIR;
949 MapNode n_junglegrass(c_junglegrass);
950 const v3s16 &em = vm->m_area.getExtent();
952 // Divide area into parts
954 s16 sidelen = central_area_size.X / div;
955 double area = sidelen * sidelen;
957 // N.B. We must add jungle grass first, since tree leaves will
958 // obstruct the ground, giving us a false ground level
959 for (s16 z0 = 0; z0 < div; z0++)
960 for (s16 x0 = 0; x0 < div; x0++) {
961 // Center position of part of division
963 node_min.X + sidelen / 2 + sidelen * x0,
964 node_min.Z + sidelen / 2 + sidelen * z0
966 // Minimum edge of part of division
968 node_min.X + sidelen * x0,
969 node_min.Z + sidelen * z0
971 // Maximum edge of part of division
973 node_min.X + sidelen + sidelen * x0 - 1,
974 node_min.Z + sidelen + sidelen * z0 - 1
977 // Get biome at center position of part of division
978 BiomeV6Type bt = getBiome(p2d_center);
982 if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
983 tree_count = area * getTreeAmount(p2d_center);
991 if (bt == BT_JUNGLE) {
992 float humidity = getHumidity(p2d_center);
993 u32 grass_count = 5 * humidity * tree_count;
994 for (u32 i = 0; i < grass_count; i++) {
995 s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
996 s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
997 int mapindex = central_area_size.X * (z - node_min.Z)
999 s16 y = heightmap[mapindex];
1000 if (y < water_level)
1003 u32 vi = vm->m_area.index(x, y, z);
1004 // place on dirt_with_grass, since we know it is exposed to sunlight
1005 if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
1006 vm->m_area.add_y(em, vi, 1);
1007 vm->m_data[vi] = n_junglegrass;
1012 // Put trees in random places on part of division
1013 for (u32 i = 0; i < tree_count; i++) {
1014 s16 x = myrand_range(p2d_min.X, p2d_max.X);
1015 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
1016 int mapindex = central_area_size.X * (z - node_min.Z)
1018 s16 y = heightmap[mapindex];
1019 // Don't make a tree under water level
1020 // Don't make a tree so high that it doesn't fit
1021 if (y < water_level || y > node_max.Y - 6)
1025 // Trees grow only on mud and grass
1027 u32 i = vm->m_area.index(p);
1028 content_t c = vm->m_data[i].getContent();
1030 c != c_dirt_with_grass &&
1031 c != c_dirt_with_snow)
1037 if (bt == BT_JUNGLE) {
1038 treegen::make_jungletree(*vm, p, ndef, myrand());
1039 } else if (bt == BT_TAIGA) {
1040 treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
1041 } else if (bt == BT_NORMAL) {
1042 bool is_apple_tree = (myrand_range(0, 3) == 0) &&
1043 getHaveAppleTree(v2s16(x, z));
1044 treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
1048 //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
1052 void MapgenV6::growGrass() // Add surface nodes
1054 MapNode n_dirt_with_grass(c_dirt_with_grass);
1055 MapNode n_dirt_with_snow(c_dirt_with_snow);
1056 MapNode n_snowblock(c_snowblock);
1057 MapNode n_snow(c_snow);
1058 const v3s16 &em = vm->m_area.getExtent();
1061 for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
1062 for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
1063 // Find the lowest surface to which enough light ends up to make
1064 // grass grow. Basically just wait until not air and not leaves.
1067 u32 i = vm->m_area.index(x, node_max.Y, z);
1069 // Go to ground level
1070 for (y = node_max.Y; y >= full_node_min.Y; y--) {
1071 MapNode &n = vm->m_data[i];
1072 if (ndef->get(n).param_type != CPT_LIGHT ||
1073 ndef->get(n).liquid_type != LIQUID_NONE ||
1074 n.getContent() == c_ice)
1076 vm->m_area.add_y(em, i, -1);
1078 surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
1081 BiomeV6Type bt = getBiome(index, v2s16(x, z));
1082 u32 i = vm->m_area.index(x, surface_y, z);
1083 content_t c = vm->m_data[i].getContent();
1084 if (surface_y >= water_level - 20) {
1085 if (bt == BT_TAIGA && c == c_dirt) {
1086 vm->m_data[i] = n_dirt_with_snow;
1087 } else if (bt == BT_TUNDRA) {
1089 vm->m_data[i] = n_snowblock;
1090 vm->m_area.add_y(em, i, -1);
1091 vm->m_data[i] = n_dirt_with_snow;
1092 } else if (c == c_stone && surface_y < node_max.Y) {
1093 vm->m_area.add_y(em, i, 1);
1094 vm->m_data[i] = n_snowblock;
1096 } else if (c == c_dirt) {
1097 vm->m_data[i] = n_dirt_with_grass;
1104 void MapgenV6::generateCaves(int max_stone_y)
1106 float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
1107 int volume_nodes = (node_max.X - node_min.X + 1) *
1108 (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
1109 cave_amount = MYMAX(0.0, cave_amount);
1110 u32 caves_count = cave_amount * volume_nodes / 50000;
1111 u32 bruises_count = 1;
1112 PseudoRandom ps(blockseed + 21343);
1113 PseudoRandom ps2(blockseed + 1032);
1115 if (ps.range(1, 6) == 1)
1116 bruises_count = ps.range(0, ps.range(0, 2));
1118 if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
1123 for (u32 i = 0; i < caves_count + bruises_count; i++) {
1124 CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
1126 bool large_cave = (i >= caves_count);
1127 cave.makeCave(vm, node_min, node_max, &ps, &ps2,
1128 large_cave, max_stone_y, heightmap);