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)
61 this->m_emerge = emerge;
62 this->ystride = csize.X; //////fix this
64 this->heightmap = new s16[csize.X * csize.Z];
66 this->spflags = params->spflags;
67 this->freq_desert = params->freq_desert;
68 this->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 spflags = MGV6_JUNGLES | MGV6_SNOWBIOMES | MGV6_TREES |
151 MGV6_BIOMEBLEND | MGV6_MUDFLOW;
156 np_terrain_base = NoiseParams(-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0);
157 np_terrain_higher = NoiseParams(20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0);
158 np_steepness = NoiseParams(0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0);
159 np_height_select = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0);
160 np_mud = NoiseParams(4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0);
161 np_beach = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0);
162 np_biome = NoiseParams(0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0);
163 np_cave = NoiseParams(6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0);
164 np_humidity = NoiseParams(0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0);
165 np_trees = NoiseParams(0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0);
166 np_apple_trees = NoiseParams(0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0);
170 void MapgenV6Params::readParams(const Settings *settings)
172 settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
173 settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
174 settings->getFloatNoEx("mgv6_freq_beach", freq_beach);
176 settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base);
177 settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
178 settings->getNoiseParams("mgv6_np_steepness", np_steepness);
179 settings->getNoiseParams("mgv6_np_height_select", np_height_select);
180 settings->getNoiseParams("mgv6_np_mud", np_mud);
181 settings->getNoiseParams("mgv6_np_beach", np_beach);
182 settings->getNoiseParams("mgv6_np_biome", np_biome);
183 settings->getNoiseParams("mgv6_np_cave", np_cave);
184 settings->getNoiseParams("mgv6_np_humidity", np_humidity);
185 settings->getNoiseParams("mgv6_np_trees", np_trees);
186 settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees);
190 void MapgenV6Params::writeParams(Settings *settings) const
192 settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
193 settings->setFloat("mgv6_freq_desert", freq_desert);
194 settings->setFloat("mgv6_freq_beach", freq_beach);
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 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 vm->m_area.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)
454 } else if (d < MGV6_FREQ_SNOW + blend) {
455 if (h > MGV6_FREQ_TAIGA + blend)
466 if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
467 ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
470 if ((spflags & MGV6_JUNGLES) && h > 0.75)
478 u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
480 s32 x = p.X, y = p.Y, z = p.Z;
481 return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
485 //////////////////////// Map generator
487 void MapgenV6::makeChunk(BlockMakeData *data)
490 assert(data->vmanip);
491 assert(data->nodedef);
492 assert(data->blockpos_requested.X >= data->blockpos_min.X &&
493 data->blockpos_requested.Y >= data->blockpos_min.Y &&
494 data->blockpos_requested.Z >= data->blockpos_min.Z);
495 assert(data->blockpos_requested.X <= data->blockpos_max.X &&
496 data->blockpos_requested.Y <= data->blockpos_max.Y &&
497 data->blockpos_requested.Z <= data->blockpos_max.Z);
499 this->generating = true;
500 this->vm = data->vmanip;
501 this->ndef = data->nodedef;
503 // Hack: use minimum block coords for old code that assumes a single block
504 v3s16 blockpos_min = data->blockpos_min;
505 v3s16 blockpos_max = data->blockpos_max;
507 // Area of central chunk
508 node_min = blockpos_min * MAP_BLOCKSIZE;
509 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
511 // Full allocated area
512 full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
513 full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
515 central_area_size = node_max - node_min + v3s16(1, 1, 1);
516 assert(central_area_size.X == central_area_size.Z);
518 int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
519 * (blockpos_max.Y - blockpos_min.Y + 1)
520 * (blockpos_max.Z - blockpos_max.Z + 1);
522 volume_nodes = volume_blocks *
523 MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
525 // Create a block-specific seed
526 blockseed = get_blockseed(data->seed, full_node_min);
531 // Maximum height of the stone surface and obstacles.
532 // This is used to guide the cave generation
533 s16 stone_surface_max_y;
535 // Generate general ground level to full area
536 stone_surface_max_y = generateGround();
538 // Create initial heightmap to limit caves
539 updateHeightmap(node_min, node_max);
541 const s16 max_spread_amount = MAP_BLOCKSIZE;
542 // Limit dirt flow area by 1 because mud is flown into neighbors.
543 s16 mudflow_minpos = -max_spread_amount + 1;
544 s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
546 // Loop this part, it will make stuff look older and newer nicely
547 const u32 age_loops = 2;
548 for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
549 // Make caves (this code is relatively horrible)
550 if (flags & MG_CAVES)
551 generateCaves(stone_surface_max_y);
553 // Add mud to the central chunk
556 // Flow mud away from steep edges
557 if (spflags & MGV6_MUDFLOW)
558 flowMud(mudflow_minpos, mudflow_maxpos);
562 // Update heightmap after mudflow
563 updateHeightmap(node_min, node_max);
566 if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
570 dp.c_water = c_water_source;
571 dp.c_river_water = c_water_source;
573 dp.only_in_ground = true;
574 dp.corridor_len_min = 1;
575 dp.corridor_len_max = 13;
578 dp.y_min = -MAX_MAP_GENERATION_LIMIT;
579 dp.y_max = MAX_MAP_GENERATION_LIMIT;
582 = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.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);
616 // Add top and bottom side of water to transforming_liquid queue
617 updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
622 // Generate some trees, and add grass, if a jungle
623 if (spflags & MGV6_TREES)
624 placeTreesAndJungleGrass();
626 // Generate the registered decorations
627 if (flags & MG_DECORATIONS)
628 m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
630 // Generate the registered ores
631 m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
633 // Calculate lighting
634 if (flags & MG_LIGHT)
635 calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
636 node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
637 full_node_min, full_node_max);
639 this->generating = false;
643 void MapgenV6::calculateNoise()
647 int fx = full_node_min.X;
648 int fz = full_node_min.Z;
650 if (!(spflags & MGV6_FLAT)) {
651 noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
652 noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
653 noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
654 noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
655 noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
658 noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
660 noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
661 noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
662 // Humidity map does not need range limiting 0 to 1,
663 // only humidity at point does
667 int MapgenV6::generateGround()
669 //TimeTaker timer1("Generating ground level");
670 MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
671 MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
672 MapNode n_ice(c_ice);
673 int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
676 for (s16 z = node_min.Z; z <= node_max.Z; z++)
677 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
679 s16 surface_y = (s16)baseTerrainLevelFromMap(index);
682 if (surface_y > stone_surface_max_y)
683 stone_surface_max_y = surface_y;
685 BiomeV6Type bt = getBiome(v2s16(x, z));
687 // Fill ground with stone
688 v3s16 em = vm->m_area.getExtent();
689 u32 i = vm->m_area.index(x, node_min.Y, z);
690 for (s16 y = node_min.Y; y <= node_max.Y; y++) {
691 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
692 if (y <= surface_y) {
693 vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
694 && bt == BT_DESERT) ?
695 n_desert_stone : n_stone;
696 } else if (y <= water_level) {
697 vm->m_data[i] = (y >= MGV6_ICE_BASE
698 && bt == BT_TUNDRA) ?
699 n_ice : n_water_source;
701 vm->m_data[i] = n_air;
704 vm->m_area.add_y(em, i, 1);
708 return stone_surface_max_y;
712 void MapgenV6::addMud()
715 //TimeTaker timer1("add mud");
716 MapNode n_dirt(c_dirt), n_gravel(c_gravel);
717 MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
721 for (s16 z = node_min.Z; z <= node_max.Z; z++)
722 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
723 // Randomize mud amount
724 s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
727 s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
729 // Handle area not found
730 if (surface_y == vm->m_area.MinEdge.Y - 1)
733 BiomeV6Type bt = getBiome(v2s16(x, z));
734 addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
736 if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
738 } else if (mud_add_amount <= 0) {
739 mud_add_amount = 1 - mud_add_amount;
741 } else if (bt != BT_DESERT && getHaveBeach(index) &&
742 surface_y + mud_add_amount <= water_level + 2) {
746 if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
747 mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
749 /* If topmost node is grass, change it to mud. It might be if it was
750 // flown to there from a neighboring chunk and then converted.
751 u32 i = vm->m_area.index(x, surface_y, z);
752 if (vm->m_data[i].getContent() == c_dirt_with_grass)
753 vm->m_data[i] = n_dirt;*/
757 v3s16 em = vm->m_area.getExtent();
758 s16 y_start = surface_y + 1;
759 u32 i = vm->m_area.index(x, y_start, z);
760 for (s16 y = y_start; y <= node_max.Y; y++) {
761 if (mudcount >= mud_add_amount)
764 vm->m_data[i] = addnode;
767 vm->m_area.add_y(em, i, 1);
773 void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
776 //TimeTaker timer1("flow mud");
778 // Iterate a few times
779 for (s16 k = 0; k < 3; k++) {
780 for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
781 for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
782 // Invert coordinates every 2nd iteration
784 x = mudflow_maxpos - (x - mudflow_minpos);
785 z = mudflow_maxpos - (z - mudflow_minpos);
788 // Node position in 2d
789 v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
791 v3s16 em = vm->m_area.getExtent();
792 u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
795 while (y >= node_min.Y) {
800 for (; y >= node_min.Y; y--) {
802 if (n->getContent() == c_dirt ||
803 n->getContent() == c_dirt_with_grass ||
804 n->getContent() == c_gravel)
807 vm->m_area.add_y(em, i, -1);
810 // Stop if out of area
811 //if(vmanip.m_area.contains(i) == false)
815 if (n->getContent() == c_dirt ||
816 n->getContent() == c_dirt_with_grass) {
817 // Make it exactly mud
818 n->setContent(c_dirt);
820 // Don't flow it if the stuff under it is not mud
823 vm->m_area.add_y(em, i2, -1);
824 // Cancel if out of area
825 if (vm->m_area.contains(i2) == false)
827 MapNode *n2 = &vm->m_data[i2];
828 if (n2->getContent() != c_dirt &&
829 n2->getContent() != c_dirt_with_grass)
835 v3s16(0, 0, 1), // back
836 v3s16(1, 0, 0), // right
837 v3s16(0, 0, -1), // front
838 v3s16(-1, 0, 0), // left
841 // Check that upper is walkable. Cancel
842 // dropping if upper keeps it in place.
844 vm->m_area.add_y(em, i3, 1);
847 if (vm->m_area.contains(i3)) {
848 n3 = &vm->m_data[i3];
849 if (ndef->get(*n3).walkable)
854 for (u32 di = 0; di < 4; di++) {
855 v3s16 dirp = dirs4[di];
858 vm->m_area.add_p(em, i2, dirp);
859 // Fail if out of area
860 if (vm->m_area.contains(i2) == false)
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 vm->m_area.add_y(em, i2, -1);
868 if (vm->m_area.contains(i2) == false)
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 vm->m_area.add_y(em, i2, -1);
877 n2 = &vm->m_data[i2];
878 // if out of known area
879 if (vm->m_area.contains(i2) == false ||
880 n2->getContent() == CONTENT_IGNORE) {
881 dropped_to_unknown = true;
884 } while (ndef->get(*n2).walkable == false);
885 // Loop one up so that we're in air
886 vm->m_area.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 vm->m_area.add_y(em, above_remove_index, 1);
928 // Mud placed may have partially-buried a stacked decoration, search
930 vm->m_area.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 vm->m_area.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 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 vm->m_area.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 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 vm->m_area.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 vm->m_area.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 vm->m_area.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);