void MapgenValleys::generateCaves(s16 max_stone_y)
{
	if (max_stone_y < node_min.Y)
		return;

	noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
	noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);

	PseudoRandom ps(blockseed + 72202);

	MapNode n_air(CONTENT_AIR);
	MapNode n_lava(c_lava_source);
	MapNode n_water(c_river_water_source);

	v3s16 em = vm->m_area.getExtent();

	// Cave blend distance near YMIN, YMAX
	const float massive_cave_blend = 128.f;
	// noise threshold for massive caves
	const float massive_cave_threshold = 0.6f;
	// mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume.

	float yblmin = -map_gen_limit + massive_cave_blend * 1.5f;
	float yblmax = massive_cave_depth - massive_cave_blend * 1.5f;
	bool made_a_big_one = false;

	// Cache the tcave values as they only vary by altitude.
	if (node_max.Y <= massive_cave_depth) {
		noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);

		for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
			float tcave = massive_cave_threshold;

			if (y < yblmin) {
				float t = (yblmin - y) / massive_cave_blend;
				tcave += MYSQUARE(t);
			} else if (y > yblmax) {
				float t = (y - yblmax) / massive_cave_blend;
				tcave += MYSQUARE(t);
			}

			tcave_cache[y - node_min.Y + 1] = tcave;
		}
	}

	// lava_depth varies between one and ten as you approach
	//  the bottom of the world.
	s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / map_gen_limit);
	// This allows random lava spawns to be less common at the surface.
	s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth;
	// water_depth varies between ten and one on the way down.
	s16 water_depth = ceil((map_gen_limit - abs(node_min.Y) + 1) * 10.f / map_gen_limit);
	// This allows random water spawns to be more common at the surface.
	s16 water_chance = MYCUBE(water_features_lim) * water_depth;

	// Reduce the odds of overflows even further.
	if (node_max.Y > water_level) {
		lava_chance /= 5;
		water_chance /= 5;
	}

	u32 index_2d = 0;
	u32 index_3d = 0;
	for (s16 z = node_min.Z; z <= node_max.Z; z++)
	for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
		Biome *biome = (Biome *)bmgr->getRaw(biomemap[index_2d]);
		bool air_above = false;
		bool underground = false;
		u32 index_data = vm->m_area.index(x, node_max.Y + 1, z);

		index_3d = (z - node_min.Z) * zstride + (csize.Y + 1) * ystride + (x - node_min.X);

		// Dig caves on down loop to check for air above.
		for (s16 y = node_max.Y + 1;
				y >= node_min.Y - 1;
				y--, index_3d -= ystride, vm->m_area.add_y(em, index_data, -1)) {
			float terrain = noise_terrain_height->result[index_2d];

			// Saves some time.
			if (y > terrain + 10) {
				air_above = true;
				continue;
			} else if (y < terrain - 40) {
				underground = true;
			}

			// Dig massive caves.
			if (node_max.Y <= massive_cave_depth
					&& noise_massive_caves->result[index_3d]
					> tcave_cache[y - node_min.Y + 1]) {
				vm->m_data[index_data] = n_air;
				made_a_big_one = true;
			}

			content_t c = vm->m_data[index_data].getContent();
			float d1 = contour(noise_cave1->result[index_3d]);
			float d2 = contour(noise_cave2->result[index_3d]);

			// River water is not set as ground content
			// in the default game. This can produce strange results
			// when a cave undercuts a river. However, that's not for
			// the mapgen to correct. Fix it in lua.

			if (c == CONTENT_AIR) {
				air_above = true;
			} else if (d1 * d2 > 0.3f && ndef->get(c).is_ground_content) {
				// in a cave
				vm->m_data[index_data] = n_air;
				air_above = true;
			} else if (air_above && (c == biome->c_filler || c == biome->c_stone)) {
				// at the cave floor
				s16 sr = ps.range(0,39);
				u32 j = index_data;
				vm->m_area.add_y(em, j, 1);

				if (sr > terrain - y) {
					// Put dirt in caves near the surface.
					if (underground)
						vm->m_data[index_data] = MapNode(biome->c_filler);
					else
						vm->m_data[index_data] = MapNode(biome->c_top);
				} else if (sr < 3 && underground) {
					sr = abs(ps.next());
					if (lava_features_lim > 0 && y <= lava_max_height
							&& c == biome->c_stone && sr < lava_chance)
						vm->m_data[j] = n_lava;

					sr -= lava_chance;

					// If sr < 0 then we should have already placed lava --
					// don't immediately dump water on it.
					if (water_features_lim > 0 && y <= cave_water_max_height
							&& sr >= 0 && sr < water_chance)
						vm->m_data[j] = n_water;
				}

				air_above = false;
				underground = true;
			} else if (c == biome->c_filler || c == biome->c_stone) {
				air_above = false;
				underground = true;
			} else {
				air_above = false;
			}
		}
	}

	if (node_max.Y <= large_cave_depth && (!made_a_big_one)) {
		u32 bruises_count = ps.range(0, 2);
		for (u32 i = 0; i < bruises_count; i++) {
			CaveV5 cave(this, &ps);
			cave.makeCave(node_min, node_max, max_stone_y);
		}
	}
}
Exemple #2
0
void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth)
{
	if (max_stone_y < node_min.Y)
		return;

	noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
	noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);

	PseudoRandom ps(blockseed + 72202);

	MapNode n_air(CONTENT_AIR);
	MapNode n_lava(c_lava_source);
	MapNode n_water(c_river_water_source);

	v3s16 em = vm->m_area.getExtent();

	// Cave blend distance near YMIN, YMAX
	const float massive_cave_blend = 128.f;
	// noise threshold for massive caves
	const float massive_cave_threshold = 0.6f;
	// mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume.

	float yblmin = -map_gen_limit + massive_cave_blend * 1.5f;
	float yblmax = massive_cave_depth - massive_cave_blend * 1.5f;
	bool made_a_big_one = false;

	// Cache the tcave values as they only vary by altitude.
	if (node_max.Y <= massive_cave_depth) {
		noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);

		for (s16 y = node_min.Y - 1; y <= node_max.Y; y++) {
			float tcave = massive_cave_threshold;

			if (y < yblmin) {
				float t = (yblmin - y) / massive_cave_blend;
				tcave += MYSQUARE(t);
			} else if (y > yblmax) {
				float t = (y - yblmax) / massive_cave_blend;
				tcave += MYSQUARE(t);
			}

			tcave_cache[y - node_min.Y + 1] = tcave;
		}
	}

	// lava_depth varies between one and ten as you approach
	//  the bottom of the world.
	s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / map_gen_limit);
	// This allows random lava spawns to be less common at the surface.
	s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth;
	// water_depth varies between ten and one on the way down.
	s16 water_depth = ceil((map_gen_limit - abs(node_min.Y) + 1) * 10.f / map_gen_limit);
	// This allows random water spawns to be more common at the surface.
	s16 water_chance = MYCUBE(water_features_lim) * water_depth;

	// Reduce the odds of overflows even further.
	if (node_max.Y > water_level) {
		lava_chance /= 3;
		water_chance /= 3;
	}

	u32 index_2d = 0;
	for (s16 z = node_min.Z; z <= node_max.Z; z++)
	for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
		Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]);
		bool tunnel_air_above = false;
		bool is_under_river = false;
		bool underground = false;
		u32 index_data = vm->m_area.index(x, node_max.Y, z);
		u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X);

		// Dig caves on down loop to check for air above.
		// Don't excavate the overgenerated stone at node_max.Y + 1,
		// this creates a 'roof' over the tunnel, preventing light in
		// tunnels at mapchunk borders when generating mapchunks upwards.
		// This 'roof' is removed when the mapchunk above is generated.
		for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
				index_3d -= ystride,
				vm->m_area.add_y(em, index_data, -1)) {

			float terrain = noise_terrain_height->result[index_2d];

			// Saves some time.
			if (y > terrain + 10)
				continue;
			else if (y < terrain - 40)
				underground = true;

			// Dig massive caves.
			if (node_max.Y <= massive_cave_depth
					&& noise_massive_caves->result[index_3d]
					> tcave_cache[y - node_min.Y + 1]) {
				vm->m_data[index_data] = n_air;
				made_a_big_one = true;
				continue;
			}

			content_t c = vm->m_data[index_data].getContent();
			// Detect river water to place riverbed nodes in tunnels
			if (c == biome->c_river_water)
				is_under_river = true;

			float d1 = contour(noise_cave1->result[index_3d]);
			float d2 = contour(noise_cave2->result[index_3d]);

			if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
				// in a tunnel
				vm->m_data[index_data] = n_air;
				tunnel_air_above = true;
			} else if (c == biome->c_filler || c == biome->c_stone) {
				if (tunnel_air_above) {
					// at the tunnel floor
					s16 sr = ps.range(0, 39);
					u32 j = index_data;
					vm->m_area.add_y(em, j, 1);

					if (sr > terrain - y) {
						// Put biome nodes in tunnels near the surface
						if (is_under_river)
							vm->m_data[index_data] = MapNode(biome->c_riverbed);
						else if (underground)
							vm->m_data[index_data] = MapNode(biome->c_filler);
						else
							vm->m_data[index_data] = MapNode(biome->c_top);
					} else if (sr < 3 && underground) {
						sr = abs(ps.next());
						if (lava_features_lim > 0 && y <= lava_max_height
								&& c == biome->c_stone && sr < lava_chance)
							vm->m_data[j] = n_lava;

						sr -= lava_chance;

						// If sr < 0 then we should have already placed lava --
						// don't immediately dump water on it.
						if (water_features_lim > 0 && y <= cave_water_max_height
								&& sr >= 0 && sr < water_chance)
							vm->m_data[j] = n_water;
					}
				}

				tunnel_air_above = false;
				underground = true;
			} else {
				tunnel_air_above = false;
			}
		}
	}

	if (node_max.Y <= large_cave_depth && !made_a_big_one) {
		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, c_lava_source);

			cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
		}
	}
}