feat: add deepwater generation pass and extend tile types for improved terrain detail
Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
parent
2133af991a
commit
6b9d6c2463
@ -22,6 +22,8 @@ BmpColors::Color get_tile_color(istd::BaseTileType type) {
|
||||
return BmpColors::WATER;
|
||||
case istd::BaseTileType::Ice:
|
||||
return BmpColors::ICE;
|
||||
case istd::BaseTileType::Deepwater:
|
||||
return BmpColors::DEEPWATER;
|
||||
default:
|
||||
return BmpColors::Color(128, 128, 128); // Gray for unknown types
|
||||
}
|
||||
@ -78,7 +80,9 @@ void generate_bmp(const istd::TileMap &tilemap, const std::string &filename) {
|
||||
|
||||
// Print statistics about the generated map
|
||||
void print_statistics(const istd::TileMap &tilemap) {
|
||||
int tile_counts[5] = {0}; // Count for each base tile type
|
||||
int tile_counts[6] = {
|
||||
0
|
||||
}; // Count for each base tile type (now 6 types including Deepwater)
|
||||
const int chunks_per_side = tilemap.get_size();
|
||||
const int tiles_per_chunk = istd::Chunk::size;
|
||||
|
||||
@ -95,13 +99,14 @@ void print_statistics(const istd::TileMap &tilemap) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *tile_names[] = {"Land", "Mountain", "Sand", "Water", "Ice"};
|
||||
const char *tile_names[]
|
||||
= {"Land", "Mountain", "Sand", "Water", "Ice", "Deepwater"};
|
||||
int total_tiles
|
||||
= chunks_per_side * chunks_per_side * tiles_per_chunk * tiles_per_chunk;
|
||||
|
||||
std::println("\nTile Statistics:");
|
||||
std::println("================");
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
double percentage = (double)tile_counts[i] / total_tiles * 100.0;
|
||||
std::println(
|
||||
"{:>10}: {:>8} ({:.1f}%)", tile_names[i], tile_counts[i], percentage
|
||||
|
@ -234,6 +234,7 @@ constexpr Color MOUNTAIN(139, 69, 19); // Saddle brown
|
||||
constexpr Color SAND(244, 164, 96); // Sandy brown
|
||||
constexpr Color WATER(30, 144, 255); // Dodger blue
|
||||
constexpr Color ICE(176, 224, 230); // Powder blue
|
||||
constexpr Color DEEPWATER(0, 0, 139); // Dark blue
|
||||
} // namespace BmpColors
|
||||
|
||||
#endif // BMP_H
|
||||
|
@ -36,6 +36,7 @@ struct GenerationConfig {
|
||||
std::uint32_t mountain_remove_threshold
|
||||
= 10; // Threshold for mountain removal
|
||||
std::uint32_t fill_threshold = 10; // Fill holes smaller than this size
|
||||
std::uint32_t deepwater_radius = 2; // Radius for deepwater generation
|
||||
};
|
||||
|
||||
class BiomeGenerationPass {
|
||||
@ -264,6 +265,66 @@ private:
|
||||
* @param tilemap The tilemap to process
|
||||
*/
|
||||
void hole_fill_pass(TileMap &tilemap);
|
||||
|
||||
/**
|
||||
* @brief Generate deepwater tiles in ocean biomes
|
||||
* @param tilemap The tilemap to process
|
||||
*/
|
||||
void deepwater_pass(TileMap &tilemap);
|
||||
};
|
||||
|
||||
class DeepwaterGenerationPass {
|
||||
private:
|
||||
std::uint32_t deepwater_radius_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a deepwater generation pass
|
||||
* @param deepwater_radius Radius to check for water tiles around each water
|
||||
* tile (default: 1)
|
||||
*/
|
||||
explicit DeepwaterGenerationPass(std::uint32_t deepwater_radius);
|
||||
|
||||
/**
|
||||
* @brief Generate deepwater tiles in ocean biomes
|
||||
* @param tilemap The tilemap to process
|
||||
*/
|
||||
void operator()(TileMap &tilemap);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Check if a tile position is within a certain radius and all tiles
|
||||
* are water
|
||||
* @param tilemap The tilemap to check
|
||||
* @param center_pos Center position to check around
|
||||
* @param radius Radius to check within
|
||||
* @return True if all tiles within radius are water
|
||||
*/
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Process an ocean sub-chunk to generate deepwater tiles
|
||||
* @param tilemap The tilemap to process
|
||||
* @param chunk_x Chunk X coordinate
|
||||
* @param chunk_y Chunk Y coordinate
|
||||
* @param sub_pos Sub-chunk position within the chunk
|
||||
*/
|
||||
void process_ocean_subchunk(
|
||||
TileMap &tilemap, std::uint8_t chunk_x, std::uint8_t chunk_y,
|
||||
SubChunkPos sub_pos
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Check if a tile position is within a certain radius and all tiles
|
||||
* are water or deepwater
|
||||
* @param tilemap The tilemap to check
|
||||
* @param center_pos Center position to check around
|
||||
* @param radius Radius to check within
|
||||
* @return True if all tiles within radius are water or deepwater
|
||||
*/
|
||||
bool is_surrounded_by_water(
|
||||
const TileMap &tilemap, TilePos center_pos, std::uint32_t radius
|
||||
) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -12,14 +12,12 @@ enum class BaseTileType : std::uint8_t {
|
||||
Sand,
|
||||
Water,
|
||||
Ice,
|
||||
Deepwater,
|
||||
_count
|
||||
};
|
||||
|
||||
enum class SurfaceTileType : std::uint8_t {
|
||||
Empty,
|
||||
Wood,
|
||||
Structure, // Indicates this tile is occupied by a player-built structure,
|
||||
// should never be natually generated.
|
||||
_count
|
||||
};
|
||||
|
||||
|
@ -492,6 +492,7 @@ void TerrainGenerator::operator()(TileMap &tilemap) {
|
||||
base_tile_type_pass(tilemap);
|
||||
smoothen_mountains_pass(tilemap);
|
||||
hole_fill_pass(tilemap);
|
||||
deepwater_pass(tilemap);
|
||||
}
|
||||
|
||||
void TerrainGenerator::biome_pass(TileMap &tilemap) {
|
||||
@ -522,6 +523,120 @@ void TerrainGenerator::hole_fill_pass(TileMap &tilemap) {
|
||||
pass(tilemap);
|
||||
}
|
||||
|
||||
void TerrainGenerator::deepwater_pass(TileMap &tilemap) {
|
||||
DeepwaterGenerationPass pass(config_.deepwater_radius);
|
||||
pass(tilemap);
|
||||
}
|
||||
|
||||
DeepwaterGenerationPass::DeepwaterGenerationPass(std::uint32_t deepwater_radius)
|
||||
: deepwater_radius_(deepwater_radius) {}
|
||||
|
||||
void DeepwaterGenerationPass::operator()(TileMap &tilemap) {
|
||||
std::uint8_t map_size = tilemap.get_size();
|
||||
|
||||
// Iterate through all sub-chunks to check biomes efficiently
|
||||
for (std::uint8_t chunk_x = 0; chunk_x < map_size; ++chunk_x) {
|
||||
for (std::uint8_t chunk_y = 0; chunk_y < map_size; ++chunk_y) {
|
||||
const Chunk &chunk = tilemap.get_chunk(chunk_x, chunk_y);
|
||||
|
||||
// Process each sub-chunk
|
||||
for (std::uint8_t sub_x = 0; sub_x < Chunk::subchunk_count;
|
||||
++sub_x) {
|
||||
for (std::uint8_t sub_y = 0; sub_y < Chunk::subchunk_count;
|
||||
++sub_y) {
|
||||
SubChunkPos sub_pos(sub_x, sub_y);
|
||||
BiomeType biome = chunk.get_biome(sub_pos);
|
||||
const BiomeProperties &properties
|
||||
= get_biome_properties(biome);
|
||||
|
||||
// Only process ocean biomes
|
||||
if (!properties.is_ocean) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process all tiles in this ocean sub-chunk
|
||||
process_ocean_subchunk(tilemap, chunk_x, chunk_y, sub_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeepwaterGenerationPass::process_ocean_subchunk(
|
||||
TileMap &tilemap, std::uint8_t chunk_x, std::uint8_t chunk_y,
|
||||
SubChunkPos sub_pos
|
||||
) {
|
||||
// Get starting tile coordinates for this sub-chunk
|
||||
auto [start_x, start_y] = subchunk_to_tile_start(sub_pos);
|
||||
|
||||
// Process all tiles in this sub-chunk
|
||||
for (std::uint8_t local_x = start_x;
|
||||
local_x < start_x + Chunk::subchunk_size; ++local_x) {
|
||||
for (std::uint8_t local_y = start_y;
|
||||
local_y < start_y + Chunk::subchunk_size; ++local_y) {
|
||||
TilePos pos{chunk_x, chunk_y, local_x, local_y};
|
||||
|
||||
// Get the tile at this position
|
||||
const Tile &tile = tilemap.get_tile(pos);
|
||||
|
||||
// Only process water tiles
|
||||
if (tile.base != BaseTileType::Water) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this water tile is surrounded by water/deepwater within
|
||||
// the specified radius
|
||||
if (is_surrounded_by_water(tilemap, pos, deepwater_radius_)) {
|
||||
// Replace water with deepwater
|
||||
Tile new_tile = tile;
|
||||
new_tile.base = BaseTileType::Deepwater;
|
||||
tilemap.set_tile(pos, new_tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DeepwaterGenerationPass::is_surrounded_by_water(
|
||||
const TileMap &tilemap, TilePos center_pos, std::uint32_t radius
|
||||
) const {
|
||||
auto [center_global_x, center_global_y] = center_pos.to_global();
|
||||
std::uint8_t map_size = tilemap.get_size();
|
||||
std::uint32_t max_global_coord = map_size * Chunk::size;
|
||||
|
||||
// Check all tiles within the radius
|
||||
for (std::int32_t dx = -static_cast<std::int32_t>(radius);
|
||||
dx <= static_cast<std::int32_t>(radius); ++dx) {
|
||||
for (std::int32_t dy = -static_cast<std::int32_t>(radius);
|
||||
dy <= static_cast<std::int32_t>(radius); ++dy) {
|
||||
std::int32_t check_x
|
||||
= static_cast<std::int32_t>(center_global_x) + dx;
|
||||
std::int32_t check_y
|
||||
= static_cast<std::int32_t>(center_global_y) + dy;
|
||||
|
||||
// Check bounds
|
||||
if (check_x < 0 || check_y < 0
|
||||
|| check_x >= static_cast<std::int32_t>(max_global_coord)
|
||||
|| check_y >= static_cast<std::int32_t>(max_global_coord)) {
|
||||
return false; // Out of bounds, consider as non-water
|
||||
}
|
||||
|
||||
// Convert back to TilePos and check if it's water
|
||||
TilePos check_pos = TilePos::from_global(
|
||||
static_cast<std::uint16_t>(check_x),
|
||||
static_cast<std::uint16_t>(check_y)
|
||||
);
|
||||
|
||||
const Tile &check_tile = tilemap.get_tile(check_pos);
|
||||
if (check_tile.base != BaseTileType::Water
|
||||
&& check_tile.base != BaseTileType::Deepwater) {
|
||||
return false; // Found non-water tile within radius
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // All tiles within radius are water or deepwater
|
||||
}
|
||||
|
||||
void map_generate(TileMap &tilemap, const GenerationConfig &config) {
|
||||
TerrainGenerator generator(config);
|
||||
generator(tilemap);
|
||||
|
Loading…
x
Reference in New Issue
Block a user