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;
|
return BmpColors::WATER;
|
||||||
case istd::BaseTileType::Ice:
|
case istd::BaseTileType::Ice:
|
||||||
return BmpColors::ICE;
|
return BmpColors::ICE;
|
||||||
|
case istd::BaseTileType::Deepwater:
|
||||||
|
return BmpColors::DEEPWATER;
|
||||||
default:
|
default:
|
||||||
return BmpColors::Color(128, 128, 128); // Gray for unknown types
|
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
|
// Print statistics about the generated map
|
||||||
void print_statistics(const istd::TileMap &tilemap) {
|
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 chunks_per_side = tilemap.get_size();
|
||||||
const int tiles_per_chunk = istd::Chunk::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
|
int total_tiles
|
||||||
= chunks_per_side * chunks_per_side * tiles_per_chunk * tiles_per_chunk;
|
= chunks_per_side * chunks_per_side * tiles_per_chunk * tiles_per_chunk;
|
||||||
|
|
||||||
std::println("\nTile Statistics:");
|
std::println("\nTile Statistics:");
|
||||||
std::println("================");
|
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;
|
double percentage = (double)tile_counts[i] / total_tiles * 100.0;
|
||||||
std::println(
|
std::println(
|
||||||
"{:>10}: {:>8} ({:.1f}%)", tile_names[i], tile_counts[i], percentage
|
"{:>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 SAND(244, 164, 96); // Sandy brown
|
||||||
constexpr Color WATER(30, 144, 255); // Dodger blue
|
constexpr Color WATER(30, 144, 255); // Dodger blue
|
||||||
constexpr Color ICE(176, 224, 230); // Powder blue
|
constexpr Color ICE(176, 224, 230); // Powder blue
|
||||||
|
constexpr Color DEEPWATER(0, 0, 139); // Dark blue
|
||||||
} // namespace BmpColors
|
} // namespace BmpColors
|
||||||
|
|
||||||
#endif // BMP_H
|
#endif // BMP_H
|
||||||
|
@ -35,7 +35,8 @@ struct GenerationConfig {
|
|||||||
= 2; // Number of steps for mountain smoothing cellular automata
|
= 2; // Number of steps for mountain smoothing cellular automata
|
||||||
std::uint32_t mountain_remove_threshold
|
std::uint32_t mountain_remove_threshold
|
||||||
= 10; // Threshold for mountain removal
|
= 10; // Threshold for mountain removal
|
||||||
std::uint32_t fill_threshold = 10; // Fill holes smaller than this size
|
std::uint32_t fill_threshold = 10; // Fill holes smaller than this size
|
||||||
|
std::uint32_t deepwater_radius = 2; // Radius for deepwater generation
|
||||||
};
|
};
|
||||||
|
|
||||||
class BiomeGenerationPass {
|
class BiomeGenerationPass {
|
||||||
@ -264,6 +265,66 @@ private:
|
|||||||
* @param tilemap The tilemap to process
|
* @param tilemap The tilemap to process
|
||||||
*/
|
*/
|
||||||
void hole_fill_pass(TileMap &tilemap);
|
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,
|
Sand,
|
||||||
Water,
|
Water,
|
||||||
Ice,
|
Ice,
|
||||||
|
Deepwater,
|
||||||
_count
|
_count
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SurfaceTileType : std::uint8_t {
|
enum class SurfaceTileType : std::uint8_t {
|
||||||
Empty,
|
Empty,
|
||||||
Wood,
|
|
||||||
Structure, // Indicates this tile is occupied by a player-built structure,
|
|
||||||
// should never be natually generated.
|
|
||||||
_count
|
_count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -492,6 +492,7 @@ void TerrainGenerator::operator()(TileMap &tilemap) {
|
|||||||
base_tile_type_pass(tilemap);
|
base_tile_type_pass(tilemap);
|
||||||
smoothen_mountains_pass(tilemap);
|
smoothen_mountains_pass(tilemap);
|
||||||
hole_fill_pass(tilemap);
|
hole_fill_pass(tilemap);
|
||||||
|
deepwater_pass(tilemap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainGenerator::biome_pass(TileMap &tilemap) {
|
void TerrainGenerator::biome_pass(TileMap &tilemap) {
|
||||||
@ -522,6 +523,120 @@ void TerrainGenerator::hole_fill_pass(TileMap &tilemap) {
|
|||||||
pass(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) {
|
void map_generate(TileMap &tilemap, const GenerationConfig &config) {
|
||||||
TerrainGenerator generator(config);
|
TerrainGenerator generator(config);
|
||||||
generator(tilemap);
|
generator(tilemap);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user