+MgStoneType MapgenBasic::generateBiomes()
+{
+ // can't generate biomes without a biome generator!
+ assert(biomegen);
+ assert(biomemap);
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+ MgStoneType stone_type = MGSTONE_STONE;
+
+ noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = NULL;
+ u16 depth_top = 0;
+ u16 base_filler = 0;
+ u16 depth_water_top = 0;
+ u16 depth_riverbed = 0;
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+ // Check node at base of mapchunk above, either a node of a previously
+ // generated mapchunk or if not, a node of overgenerated base terrain.
+ content_t c_above = vm->m_data[vi + em.X].getContent();
+ bool air_above = c_above == CONTENT_AIR;
+ bool river_water_above = c_above == c_river_water_source;
+ bool water_above = c_above == c_water_source || river_water_above;
+
+ biomemap[index] = BIOME_NONE;
+
+ // If there is air or water above enable top/filler placement, otherwise force
+ // nplaced to stone level by setting a number exceeding any possible filler depth.
+ u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
+
+ for (s16 y = node_max.Y; y >= node_min.Y; y--) {
+ content_t c = vm->m_data[vi].getContent();
+
+ // Biome is recalculated each time an upper surface is detected while
+ // working down a column. The selected biome then remains in effect for
+ // all nodes below until the next surface and biome recalculation.
+ // Biome is recalculated:
+ // 1. At the surface of stone below air or water.
+ // 2. At the surface of water below air.
+ // 3. When stone or water is detected but biome has not yet been calculated.
+ bool is_stone_surface = (c == c_stone) &&
+ (air_above || water_above || !biome);
+
+ bool is_water_surface =
+ (c == c_water_source || c == c_river_water_source) &&
+ (air_above || !biome);
+
+ if (is_stone_surface || is_water_surface) {
+ biome = biomegen->getBiomeAtIndex(index, y);
+
+ if (biomemap[index] == BIOME_NONE && is_stone_surface)
+ biomemap[index] = biome->index;
+
+ depth_top = biome->depth_top;
+ base_filler = MYMAX(depth_top +
+ biome->depth_filler +
+ noise_filler_depth->result[index], 0.f);
+ depth_water_top = biome->depth_water_top;
+ depth_riverbed = biome->depth_riverbed;
+
+ // Detect stone type for dungeons during every biome calculation.
+ // This is more efficient than detecting per-node and will not
+ // miss any desert stone or sandstone biomes.
+ if (biome->c_stone == c_desert_stone)
+ stone_type = MGSTONE_DESERT_STONE;
+ else if (biome->c_stone == c_sandstone)
+ stone_type = MGSTONE_SANDSTONE;
+ }
+
+ if (c == c_stone) {
+ content_t c_below = vm->m_data[vi - em.X].getContent();
+
+ // If the node below isn't solid, make this node stone, so that
+ // any top/filler nodes above are structurally supported.
+ // This is done by aborting the cycle of top/filler placement
+ // immediately by forcing nplaced to stone level.
+ if (c_below == CONTENT_AIR
+ || c_below == c_water_source
+ || c_below == c_river_water_source)
+ nplaced = U16_MAX;
+
+ if (river_water_above) {
+ if (nplaced < depth_riverbed) {
+ vm->m_data[vi] = MapNode(biome->c_riverbed);
+ nplaced++;
+ } else {
+ nplaced = U16_MAX; // Disable top/filler placement
+ river_water_above = false;
+ }
+ } else if (nplaced < depth_top) {
+ vm->m_data[vi] = MapNode(biome->c_top);
+ nplaced++;
+ } else if (nplaced < base_filler) {
+ vm->m_data[vi] = MapNode(biome->c_filler);
+ nplaced++;
+ } else {
+ vm->m_data[vi] = MapNode(biome->c_stone);
+ }
+
+ air_above = false;
+ water_above = false;
+ } else if (c == c_water_source) {
+ vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
+ ? biome->c_water_top : biome->c_water);
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = false;
+ water_above = true;
+ } else if (c == c_river_water_source) {
+ vm->m_data[vi] = MapNode(biome->c_river_water);
+ nplaced = 0; // Enable riverbed placement for next surface
+ air_above = false;
+ water_above = true;
+ river_water_above = true;
+ } else if (c == CONTENT_AIR) {
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = true;
+ water_above = false;
+ } else { // Possible various nodes overgenerated from neighbouring mapchunks
+ nplaced = U16_MAX; // Disable top/filler placement
+ air_above = false;
+ water_above = false;
+ }
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+ }
+
+ return stone_type;
+}
+
+
+void MapgenBasic::dustTopNodes()
+{
+ if (node_max.Y < water_level)
+ return;
+
+ v3s16 em = vm->m_area.getExtent();
+ u32 index = 0;
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+ Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
+
+ if (biome->c_dust == CONTENT_IGNORE)
+ continue;
+
+ u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+ content_t c_full_max = vm->m_data[vi].getContent();
+ s16 y_start;
+
+ if (c_full_max == CONTENT_AIR) {
+ y_start = full_node_max.Y - 1;
+ } else if (c_full_max == CONTENT_IGNORE) {
+ vi = vm->m_area.index(x, node_max.Y + 1, z);
+ content_t c_max = vm->m_data[vi].getContent();
+
+ if (c_max == CONTENT_AIR)
+ y_start = node_max.Y;
+ else
+ continue;
+ } else {
+ continue;
+ }
+
+ vi = vm->m_area.index(x, y_start, z);
+ for (s16 y = y_start; y >= node_min.Y - 1; y--) {
+ if (vm->m_data[vi].getContent() != CONTENT_AIR)
+ break;
+
+ vm->m_area.add_y(em, vi, -1);
+ }
+
+ content_t c = vm->m_data[vi].getContent();
+ if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
+ vm->m_area.add_y(em, vi, 1);
+ vm->m_data[vi] = MapNode(biome->c_dust);
+ }
+ }
+}
+
+
+void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
+ &np_cave1, &np_cave2, seed, cave_width);
+
+ caves_noise.generateCaves(vm, node_min, node_max, biomemap);
+
+ if (node_max.Y > large_cave_depth)
+ return;
+
+ PseudoRandom ps(blockseed + 21343);
+ u32 bruises_count = ps.range(0, 2);
+ for (u32 i = 0; i < bruises_count; i++) {
+ CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
+ c_water_source, CONTENT_IGNORE);
+
+ cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
+ }
+}
+
+
+bool MapgenBasic::generateCaverns(s16 max_stone_y)
+{
+ if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
+ return false;
+
+ CavernsNoise caverns_noise(ndef, csize, &np_cavern,
+ seed, cavern_limit, cavern_taper, cavern_threshold);
+
+ return caverns_noise.generateCaverns(vm, node_min, node_max);
+}
+
+
+void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type)
+{
+ if (max_stone_y < node_min.Y)
+ return;
+
+ DungeonParams dp;
+
+ dp.seed = seed;
+ dp.c_water = c_water_source;
+ dp.c_river_water = c_river_water_source;
+
+ dp.only_in_ground = true;
+ dp.corridor_len_min = 1;
+ dp.corridor_len_max = 13;
+ dp.rooms_min = 2;
+ dp.rooms_max = 16;
+ dp.y_min = -MAX_MAP_GENERATION_LIMIT;
+ dp.y_max = MAX_MAP_GENERATION_LIMIT;
+
+ dp.np_density = nparams_dungeon_density;
+ dp.np_alt_wall = nparams_dungeon_alt_wall;
+
+ switch (stone_type) {
+ default:
+ case MGSTONE_STONE:
+ dp.c_wall = c_cobble;
+ dp.c_alt_wall = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.room_size_min = v3s16(4, 4, 4);
+ dp.room_size_max = v3s16(8, 6, 8);
+ dp.room_size_large_min = v3s16(8, 8, 8);
+ dp.room_size_large_max = v3s16(16, 16, 16);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ break;
+ case MGSTONE_DESERT_STONE:
+ dp.c_wall = c_desert_stone;
+ dp.c_alt_wall = CONTENT_IGNORE;
+ dp.c_stair = c_stair_desert_stone;
+
+ dp.diagonal_dirs = true;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.room_size_min = v3s16(6, 9, 6);
+ dp.room_size_max = v3s16(10, 11, 10);
+ dp.room_size_large_min = v3s16(10, 13, 10);
+ dp.room_size_large_max = v3s16(18, 21, 18);
+ dp.notifytype = GENNOTIFY_TEMPLE;
+ break;
+ case MGSTONE_SANDSTONE:
+ dp.c_wall = c_sandstonebrick;
+ dp.c_alt_wall = CONTENT_IGNORE;
+ dp.c_stair = c_stair_sandstone_block;
+
+ dp.diagonal_dirs = false;
+ dp.holesize = v3s16(2, 2, 2);
+ dp.room_size_min = v3s16(6, 4, 6);
+ dp.room_size_max = v3s16(10, 6, 10);
+ dp.room_size_large_min = v3s16(10, 8, 10);
+ dp.room_size_large_max = v3s16(18, 16, 18);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ break;