feat: add oil generation pass and update related configurations and documentation
Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
parent
6a3f23f989
commit
937a1324eb
@ -6,6 +6,7 @@ set(ISTD_TILEMAP_SRC
|
|||||||
src/pass/biome.cpp
|
src/pass/biome.cpp
|
||||||
src/pass/deepwater.cpp
|
src/pass/deepwater.cpp
|
||||||
src/pass/mountain_hole_fill.cpp
|
src/pass/mountain_hole_fill.cpp
|
||||||
|
src/pass/oil.cpp
|
||||||
src/pass/smoothen_mountain.cpp
|
src/pass/smoothen_mountain.cpp
|
||||||
src/pass/smoothen_island.cpp
|
src/pass/smoothen_island.cpp
|
||||||
src/generation.cpp
|
src/generation.cpp
|
||||||
|
@ -51,10 +51,10 @@ struct Tile {
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Base Tile Types:**
|
**Base Tile Types:**
|
||||||
- `Land`, `Mountain`, `Sand`, `Water`, `Ice`
|
- `Land`, `Mountain`, `Sand`, `Water`, `Ice`, `Deepwater`
|
||||||
|
|
||||||
**Surface Tile Types:**
|
**Surface Tile Types:**
|
||||||
- `Empty`, `Wood`, `Structure`
|
- `Empty`, `Oil`
|
||||||
|
|
||||||
### Position Types
|
### Position Types
|
||||||
|
|
||||||
@ -90,6 +90,12 @@ struct GenerationConfig {
|
|||||||
int temperature_octaves = 3;
|
int temperature_octaves = 3;
|
||||||
int humidity_octaves = 3;
|
int humidity_octaves = 3;
|
||||||
int base_octaves = 3;
|
int base_octaves = 3;
|
||||||
|
|
||||||
|
// Oil generation parameters
|
||||||
|
double oil_density = 0.8; // Average oil fields per chunk
|
||||||
|
std::uint32_t oil_cluster_min_size = 2; // Minimum tiles per cluster
|
||||||
|
std::uint32_t oil_cluster_max_size = 6; // Maximum tiles per cluster
|
||||||
|
double oil_biome_preference = 2.0; // Multiplier for preferred biomes
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -27,8 +27,10 @@ tilemap/
|
|||||||
│ ├── biome.cpp # Climate-based biome generation
|
│ ├── biome.cpp # Climate-based biome generation
|
||||||
│ ├── base_tile_type.cpp # Base terrain generation
|
│ ├── base_tile_type.cpp # Base terrain generation
|
||||||
│ ├── smoothen_mountain.cpp # Mountain smoothing
|
│ ├── smoothen_mountain.cpp # Mountain smoothing
|
||||||
|
│ ├── smoothen_island.cpp # Island smoothing
|
||||||
│ ├── mountain_hole_fill.cpp # Hole filling
|
│ ├── mountain_hole_fill.cpp # Hole filling
|
||||||
│ └── deepwater.cpp # Deep water placement
|
│ ├── deepwater.cpp # Deep water placement
|
||||||
|
│ └── oil.cpp # Oil resource generation
|
||||||
├── examples/ # Usage examples
|
├── examples/ # Usage examples
|
||||||
└── docs/ # Documentation
|
└── docs/ # Documentation
|
||||||
```
|
```
|
||||||
@ -51,8 +53,10 @@ Terrain generation uses a multi-pass pipeline for modularity and control:
|
|||||||
1. **Biome Pass**: Generates climate data and assigns biomes to sub-chunks
|
1. **Biome Pass**: Generates climate data and assigns biomes to sub-chunks
|
||||||
2. **Base Tile Type Pass**: Creates base terrain based on biomes
|
2. **Base Tile Type Pass**: Creates base terrain based on biomes
|
||||||
3. **Mountain Smoothing Pass**: Removes isolated mountain clusters
|
3. **Mountain Smoothing Pass**: Removes isolated mountain clusters
|
||||||
4. **Hole Fill Pass**: Fills small terrain holes
|
4. **Island Smoothing Pass**: Smooths island coastlines using cellular automata
|
||||||
5. **Deep Water Pass**: Places deep water areas
|
5. **Hole Fill Pass**: Fills small terrain holes
|
||||||
|
6. **Deep Water Pass**: Places deep water areas
|
||||||
|
7. **Oil Pass**: Generates sparse oil deposits as surface features
|
||||||
|
|
||||||
Each pass operates independently with its own RNG state, ensuring deterministic results.
|
Each pass operates independently with its own RNG state, ensuring deterministic results.
|
||||||
|
|
||||||
@ -83,9 +87,18 @@ The library addresses Perlin noise distribution issues:
|
|||||||
|
|
||||||
Several passes use BFS (Breadth-First Search) for terrain analysis:
|
Several passes use BFS (Breadth-First Search) for terrain analysis:
|
||||||
- **Mountain Smoothing**: Find and remove small mountain components
|
- **Mountain Smoothing**: Find and remove small mountain components
|
||||||
|
- **Island Smoothing**: Apply cellular automata for natural coastlines
|
||||||
- **Hole Filling**: Identify and fill isolated terrain holes
|
- **Hole Filling**: Identify and fill isolated terrain holes
|
||||||
- Components touching map boundaries are preserved
|
- Components touching map boundaries are preserved
|
||||||
|
|
||||||
|
### Oil Resource Generation
|
||||||
|
|
||||||
|
The oil generation pass creates sparse resource deposits:
|
||||||
|
- **Poisson Disk Sampling**: Ensures minimum distance between oil fields
|
||||||
|
- **Biome Preference**: Higher probability in desert and plains biomes
|
||||||
|
- **Cluster Growth**: Random walk creates 2-6 tile clusters
|
||||||
|
- **Surface Placement**: Oil appears as surface features on land/sand tiles
|
||||||
|
|
||||||
## Random Number Generation
|
## Random Number Generation
|
||||||
|
|
||||||
### Xoroshiro128++ Implementation
|
### Xoroshiro128++ Implementation
|
||||||
|
@ -5,13 +5,18 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <iostream>
|
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
// Get BMP color for different base tile types
|
// Get BMP color for different tile types, considering surface tiles
|
||||||
BmpColors::Color get_tile_color(istd::BaseTileType type) {
|
BmpColors::Color get_tile_color(const istd::Tile &tile) {
|
||||||
switch (type) {
|
// Oil surface tile overrides base color
|
||||||
|
if (tile.surface == istd::SurfaceTileType::Oil) {
|
||||||
|
return BmpColors::OIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise use base tile color
|
||||||
|
switch (tile.base) {
|
||||||
case istd::BaseTileType::Land:
|
case istd::BaseTileType::Land:
|
||||||
return BmpColors::LAND;
|
return BmpColors::LAND;
|
||||||
case istd::BaseTileType::Mountain:
|
case istd::BaseTileType::Mountain:
|
||||||
@ -50,7 +55,7 @@ void generate_bmp(const istd::TileMap &tilemap, const std::string &filename) {
|
|||||||
int global_x = chunk_x * tiles_per_chunk + tile_x;
|
int global_x = chunk_x * tiles_per_chunk + tile_x;
|
||||||
int global_y = chunk_y * tiles_per_chunk + tile_y;
|
int global_y = chunk_y * tiles_per_chunk + tile_y;
|
||||||
|
|
||||||
auto color = get_tile_color(tile.base);
|
auto color = get_tile_color(tile);
|
||||||
|
|
||||||
// Draw a tile_size x tile_size block
|
// Draw a tile_size x tile_size block
|
||||||
for (int dy = 0; dy < tile_size; ++dy) {
|
for (int dy = 0; dy < tile_size; ++dy) {
|
||||||
@ -83,6 +88,7 @@ void print_statistics(const istd::TileMap &tilemap) {
|
|||||||
int tile_counts[6] = {
|
int tile_counts[6] = {
|
||||||
0
|
0
|
||||||
}; // Count for each base tile type (now 6 types including Deepwater)
|
}; // Count for each base tile type (now 6 types including Deepwater)
|
||||||
|
int oil_count = 0; // Count oil surface tiles
|
||||||
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;
|
||||||
|
|
||||||
@ -94,15 +100,20 @@ void print_statistics(const istd::TileMap &tilemap) {
|
|||||||
for (int tile_y = 0; tile_y < tiles_per_chunk; ++tile_y) {
|
for (int tile_y = 0; tile_y < tiles_per_chunk; ++tile_y) {
|
||||||
const auto &tile = chunk.tiles[tile_x][tile_y];
|
const auto &tile = chunk.tiles[tile_x][tile_y];
|
||||||
tile_counts[static_cast<int>(tile.base)]++;
|
tile_counts[static_cast<int>(tile.base)]++;
|
||||||
|
|
||||||
|
// Count oil surface tiles
|
||||||
|
if (tile.surface == istd::SurfaceTileType::Oil) {
|
||||||
|
oil_count++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *tile_names[]
|
const char *tile_names[] = {"Land", "Mountain", "Sand",
|
||||||
= {"Land", "Mountain", "Sand", "Water", "Ice", "Deepwater"};
|
"Water", "Ice", "Deepwater"};
|
||||||
int total_tiles
|
int total_tiles = chunks_per_side * chunks_per_side * tiles_per_chunk
|
||||||
= chunks_per_side * chunks_per_side * tiles_per_chunk * tiles_per_chunk;
|
* tiles_per_chunk;
|
||||||
|
|
||||||
std::println("\nTile Statistics:");
|
std::println("\nTile Statistics:");
|
||||||
std::println("================");
|
std::println("================");
|
||||||
@ -112,6 +123,16 @@ void print_statistics(const istd::TileMap &tilemap) {
|
|||||||
"{:>10}: {:>8} ({:.1f}%)", tile_names[i], tile_counts[i], percentage
|
"{:>10}: {:>8} ({:.1f}%)", tile_names[i], tile_counts[i], percentage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print oil statistics
|
||||||
|
double oil_percentage = (double)oil_count / total_tiles * 100.0;
|
||||||
|
double oil_per_chunk = (double)oil_count
|
||||||
|
/ (chunks_per_side * chunks_per_side);
|
||||||
|
std::println(
|
||||||
|
"{:>10}: {:>8} ({:.1f}%, {:.2f} per chunk)", "Oil", oil_count,
|
||||||
|
oil_percentage, oil_per_chunk
|
||||||
|
);
|
||||||
|
|
||||||
std::println("Total tiles: {}", total_tiles);
|
std::println("Total tiles: {}", total_tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +152,9 @@ int main(int argc, char *argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
istd::Seed seed
|
istd::Seed seed = istd::Seed::from_string(
|
||||||
= istd::Seed::from_string(argc >= 2 ? argv[1] : "hello_world");
|
argc >= 2 ? argv[1] : "hello_world"
|
||||||
|
);
|
||||||
std::string output_filename = argc >= 3 ? argv[2] : "output.bmp";
|
std::string output_filename = argc >= 3 ? argv[2] : "output.bmp";
|
||||||
int chunks_per_side = 8; // Default value
|
int chunks_per_side = 8; // Default value
|
||||||
|
|
||||||
|
@ -235,6 +235,7 @@ 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
|
constexpr Color DEEPWATER(0, 0, 139); // Dark blue
|
||||||
|
constexpr Color OIL(0, 0, 0); // Black
|
||||||
} // namespace BmpColors
|
} // namespace BmpColors
|
||||||
|
|
||||||
#endif // BMP_H
|
#endif // BMP_H
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include "tile.h"
|
#include "tile.h"
|
||||||
#include <compare>
|
#include <compare>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace istd {
|
namespace istd {
|
||||||
|
|
||||||
@ -27,6 +29,13 @@ struct TilePos {
|
|||||||
uint8_t local_x;
|
uint8_t local_x;
|
||||||
uint8_t local_y;
|
uint8_t local_y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate squared distance to another TilePos
|
||||||
|
* @param other Other TilePos to compare with
|
||||||
|
* @return Squared distance between the two positions
|
||||||
|
*/
|
||||||
|
std::uint32_t sqr_distance_to(TilePos other) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert TilePos to global coordinates
|
* @brief Convert TilePos to global coordinates
|
||||||
* @return Pair of global X and Y coordinates
|
* @return Pair of global X and Y coordinates
|
||||||
@ -40,15 +49,23 @@ struct TilePos {
|
|||||||
* @return TilePos corresponding to the global coordinates
|
* @return TilePos corresponding to the global coordinates
|
||||||
*/
|
*/
|
||||||
static TilePos from_global(std::uint16_t global_x, std::uint16_t global_y);
|
static TilePos from_global(std::uint16_t global_x, std::uint16_t global_y);
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Three-way comparison operator for TilePos
|
* @brief Three-way comparison operator for TilePos
|
||||||
* @param lhs Left-hand side TilePos
|
* @param lhs Left-hand side TilePos
|
||||||
* @param rhs Right-hand side TilePos
|
* @param rhs Right-hand side TilePos
|
||||||
* @return Strong ordering comparison result
|
* @return Strong ordering comparison result
|
||||||
*/
|
*/
|
||||||
std::strong_ordering operator<=>(const TilePos &lhs, const TilePos &rhs);
|
friend std::strong_ordering operator<=>(TilePos lhs, TilePos rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Equality comparison operator for TilePos
|
||||||
|
* @param lhs Left-hand side TilePos
|
||||||
|
* @param rhs Right-hand side TilePos
|
||||||
|
* @return True if positions are equal
|
||||||
|
*/
|
||||||
|
friend bool operator==(TilePos lhs, TilePos rhs) = default;
|
||||||
|
};
|
||||||
|
|
||||||
struct Chunk {
|
struct Chunk {
|
||||||
// Size of a chunk in tiles (64 x 64)
|
// Size of a chunk in tiles (64 x 64)
|
||||||
@ -83,6 +100,28 @@ struct Chunk {
|
|||||||
const BiomeType &get_biome(SubChunkPos pos) const {
|
const BiomeType &get_biome(SubChunkPos pos) const {
|
||||||
return biome[pos.sub_x][pos.sub_y];
|
return biome[pos.sub_x][pos.sub_y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get biome for a specific local tile position
|
||||||
|
* @param local_x Local X coordinate within the chunk
|
||||||
|
* @param local_y Local Y coordinate within the chunk
|
||||||
|
* @return Reference to biome type
|
||||||
|
*/
|
||||||
|
const BiomeType &get_biome(
|
||||||
|
std::uint8_t local_x, std::uint8_t local_y
|
||||||
|
) const {
|
||||||
|
SubChunkPos sub_pos(local_x / subchunk_size, local_y / subchunk_size);
|
||||||
|
return get_biome(sub_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get biome for a specific TilePos
|
||||||
|
* @param pos TilePos to get the biome for
|
||||||
|
* @return Reference to biome type
|
||||||
|
*/
|
||||||
|
const BiomeType &get_biome(TilePos pos) const {
|
||||||
|
return get_biome(pos.local_x, pos.local_y);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,4 +133,14 @@ std::pair<std::uint8_t, std::uint8_t> subchunk_to_tile_start(SubChunkPos pos);
|
|||||||
|
|
||||||
} // namespace istd
|
} // namespace istd
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::hash<istd::TilePos> {
|
||||||
|
::std::size_t operator()(istd::TilePos pos) const {
|
||||||
|
return (static_cast<::std::size_t>(pos.chunk_x) << 24)
|
||||||
|
| (static_cast<::std::size_t>(pos.chunk_y) << 16)
|
||||||
|
| (static_cast<::std::size_t>(pos.local_x) << 8)
|
||||||
|
| static_cast<::std::size_t>(pos.local_y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -5,6 +5,7 @@
|
|||||||
#include "chunk.h"
|
#include "chunk.h"
|
||||||
#include "noise.h"
|
#include "noise.h"
|
||||||
#include "tilemap.h"
|
#include "tilemap.h"
|
||||||
|
#include "xoroshiro.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -40,6 +41,14 @@ struct GenerationConfig {
|
|||||||
|
|
||||||
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
|
std::uint32_t deepwater_radius = 2; // Radius for deepwater generation
|
||||||
|
|
||||||
|
// Oil generation parameters
|
||||||
|
std::uint8_t oil_density = 204; // Average oil fields per 255 chunk (~0.8)
|
||||||
|
std::uint8_t oil_cluster_min_size = 1; // Minimum tiles per oil cluster
|
||||||
|
std::uint8_t oil_cluster_max_size = 7; // Maximum tiles per oil cluster
|
||||||
|
// (should be <= 24)
|
||||||
|
std::uint8_t oil_base_probe = 128; // Biome preference multiplier (out
|
||||||
|
// of 255)
|
||||||
};
|
};
|
||||||
|
|
||||||
class BiomeGenerationPass {
|
class BiomeGenerationPass {
|
||||||
@ -351,6 +360,12 @@ private:
|
|||||||
* @param tilemap The tilemap to process
|
* @param tilemap The tilemap to process
|
||||||
*/
|
*/
|
||||||
void deepwater_pass(TileMap &tilemap);
|
void deepwater_pass(TileMap &tilemap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate oil clusters on suitable terrain
|
||||||
|
* @param tilemap The tilemap to process
|
||||||
|
*/
|
||||||
|
void oil_pass(TileMap &tilemap);
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeepwaterGenerationPass {
|
class DeepwaterGenerationPass {
|
||||||
@ -407,6 +422,67 @@ private:
|
|||||||
) const;
|
) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OilGenerationPass {
|
||||||
|
private:
|
||||||
|
const GenerationConfig &config_;
|
||||||
|
Xoroshiro128PP rng_;
|
||||||
|
DiscreteRandomNoise noise_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Construct an oil generation pass
|
||||||
|
* @param config Generation configuration parameters
|
||||||
|
* @param rng Random number generator for oil placement
|
||||||
|
* @param noise_rng Random number generator for noise-based operations
|
||||||
|
*/
|
||||||
|
OilGenerationPass(
|
||||||
|
const GenerationConfig &config, Xoroshiro128PP rng,
|
||||||
|
Xoroshiro128PP noise_rng
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate oil clusters on the tilemap
|
||||||
|
* @param tilemap The tilemap to process
|
||||||
|
*/
|
||||||
|
void operator()(TileMap &tilemap);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Generate oil center positions using Poisson disk sampling
|
||||||
|
* @param tilemap The tilemap to analyze
|
||||||
|
* @return Vector of positions where oil clusters should be placed
|
||||||
|
*/
|
||||||
|
std::vector<TilePos> generate_oil_centers(const TileMap &tilemap);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate an oil cluster around a center position
|
||||||
|
* @param tilemap The tilemap to modify
|
||||||
|
* @param center Center position for the oil cluster
|
||||||
|
*/
|
||||||
|
void generate_oil_cluster(TileMap &tilemap, TilePos center);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if a tile is suitable for oil placement
|
||||||
|
* @param tilemap The tilemap to check
|
||||||
|
* @param pos Position to check
|
||||||
|
* @return True if oil can be placed at this position
|
||||||
|
*/
|
||||||
|
bool is_suitable_for_oil(const TileMap &tilemap, TilePos pos) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get biome preference multiplier for oil generation (out of 255)
|
||||||
|
* @param biome The biome type to check
|
||||||
|
* @return Preference value (0-255)
|
||||||
|
*/
|
||||||
|
std::uint8_t get_biome_oil_preference(BiomeType biome) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate minimum distance between oil fields based on map size
|
||||||
|
* @return Minimum distance in tiles
|
||||||
|
*/
|
||||||
|
std::uint32_t calculate_min_oil_distance() const;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate a tilemap using the new biome-based system
|
* @brief Generate a tilemap using the new biome-based system
|
||||||
* @param tilemap The tilemap to generate into
|
* @param tilemap The tilemap to generate into
|
||||||
|
@ -19,28 +19,55 @@ private:
|
|||||||
std::uint64_t mask;
|
std::uint64_t mask;
|
||||||
std::array<std::uint8_t, 256> permutation_;
|
std::array<std::uint8_t, 256> permutation_;
|
||||||
|
|
||||||
std::uint8_t perm(int x) const;
|
std::uint8_t perm(int x) const noexcept;
|
||||||
std::uint32_t map(std::uint32_t x) const;
|
std::uint32_t rot8(std::uint32_t x) const noexcept;
|
||||||
|
std::uint32_t map_once(std::uint32_t x) const noexcept;
|
||||||
|
std::uint32_t map(std::uint32_t x) const noexcept;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Construct a DiscreteRandomNoise generator with the given seed
|
* @brief Construct a DiscreteRandomNoise generator with the given seed
|
||||||
* @param rng Random number generator for noise
|
* @param rng Random number generator for noise
|
||||||
*/
|
*/
|
||||||
explicit DiscreteRandomNoise(Xoroshiro128PP rng);
|
explicit DiscreteRandomNoise(Xoroshiro128PP rng) noexcept;
|
||||||
|
|
||||||
DiscreteRandomNoise() = default;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Generate a discrete random value at the given coordinates
|
* @brief Generate a discrete random value at the given coordinates
|
||||||
* @param x X coordinate
|
* @param x X coordinate
|
||||||
* @param y Y coordinate
|
* @param y Y coordinate
|
||||||
* @param z Z coordinate (optional)
|
* @param z Z coordinate (optional)
|
||||||
* @return Discrete random value between 0 and 255
|
* @return Discrete random value between 0 and 2^64-1
|
||||||
*/
|
*/
|
||||||
std::uint64_t noise(
|
std::uint64_t noise(
|
||||||
std::uint32_t x, std::uint32_t y, std::uint32_t z = 0
|
std::uint32_t x, std::uint32_t y, std::uint32_t z = 0
|
||||||
) const;
|
) const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DiscreteRandomNoiseStream {
|
||||||
|
private:
|
||||||
|
const DiscreteRandomNoise &noise_;
|
||||||
|
std::uint32_t x_;
|
||||||
|
std::uint32_t y_;
|
||||||
|
std::uint32_t idx_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DiscreteRandomNoiseStream(
|
||||||
|
const DiscreteRandomNoise &noise, std::uint32_t x, std::uint32_t y,
|
||||||
|
std::uint32_t idx = 0
|
||||||
|
);
|
||||||
|
|
||||||
|
std::uint64_t next() noexcept;
|
||||||
|
|
||||||
|
// Adaption for STL RandomEngine named requirements
|
||||||
|
using result_type = std::uint64_t;
|
||||||
|
static constexpr result_type min() noexcept {
|
||||||
|
return std::numeric_limits<result_type>::min();
|
||||||
|
}
|
||||||
|
static constexpr result_type max() noexcept {
|
||||||
|
return std::numeric_limits<result_type>::max();
|
||||||
|
}
|
||||||
|
// Equivalent to next(), for STL compatibility
|
||||||
|
result_type operator()() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PerlinNoise {
|
class PerlinNoise {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#define ISTD_TILEMAP_TILE_H
|
#define ISTD_TILEMAP_TILE_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdexcept> // For std::invalid_argument
|
|
||||||
|
|
||||||
namespace istd {
|
namespace istd {
|
||||||
|
|
||||||
@ -18,14 +17,17 @@ enum class BaseTileType : std::uint8_t {
|
|||||||
|
|
||||||
enum class SurfaceTileType : std::uint8_t {
|
enum class SurfaceTileType : std::uint8_t {
|
||||||
Empty,
|
Empty,
|
||||||
|
Oil,
|
||||||
_count
|
_count
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::uint8_t base_tile_count
|
constexpr std::uint8_t base_tile_count = static_cast<std::uint8_t>(
|
||||||
= static_cast<std::uint8_t>(BaseTileType::_count);
|
BaseTileType::_count
|
||||||
|
);
|
||||||
|
|
||||||
constexpr std::uint8_t surface_tile_count
|
constexpr std::uint8_t surface_tile_count = static_cast<std::uint8_t>(
|
||||||
= static_cast<std::uint8_t>(SurfaceTileType::_count);
|
SurfaceTileType::_count
|
||||||
|
);
|
||||||
|
|
||||||
static_assert(base_tile_count <= 16, "Base tile don't fit in 4 bits");
|
static_assert(base_tile_count <= 16, "Base tile don't fit in 4 bits");
|
||||||
static_assert(surface_tile_count <= 16, "Surface tile don't fit in 4 bits");
|
static_assert(surface_tile_count <= 16, "Surface tile don't fit in 4 bits");
|
||||||
|
@ -34,6 +34,13 @@ public:
|
|||||||
Chunk &get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y);
|
Chunk &get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y);
|
||||||
const Chunk &get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y) const;
|
const Chunk &get_chunk(std::uint8_t chunk_x, std::uint8_t chunk_y) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get a reference to the chunk containing the given TilePos
|
||||||
|
* @param pos The TilePos to get the chunk for
|
||||||
|
*/
|
||||||
|
Chunk &get_chunk_of(TilePos pos);
|
||||||
|
const Chunk &get_chunk_of(TilePos pos) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a tile at the given position
|
* @brief Get a tile at the given position
|
||||||
* @param pos The position of the tile
|
* @param pos The position of the tile
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#include "chunk.h"
|
#include "chunk.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace istd {
|
namespace istd {
|
||||||
|
|
||||||
std::strong_ordering operator<=>(const TilePos &lhs, const TilePos &rhs) {
|
std::strong_ordering operator<=>(TilePos lhs, TilePos rhs) {
|
||||||
if (lhs.chunk_x != rhs.chunk_x) {
|
if (lhs.chunk_x != rhs.chunk_x) {
|
||||||
return lhs.chunk_x <=> rhs.chunk_x;
|
return lhs.chunk_x <=> rhs.chunk_x;
|
||||||
}
|
}
|
||||||
@ -23,6 +24,24 @@ std::pair<std::uint8_t, std::uint8_t> subchunk_to_tile_start(SubChunkPos pos) {
|
|||||||
return {pos.sub_x * Chunk::subchunk_size, pos.sub_y * Chunk::subchunk_size};
|
return {pos.sub_x * Chunk::subchunk_size, pos.sub_y * Chunk::subchunk_size};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint32_t TilePos::sqr_distance_to(TilePos other) const {
|
||||||
|
auto [this_global_x, this_global_y] = to_global();
|
||||||
|
auto [other_global_x, other_global_y] = other.to_global();
|
||||||
|
|
||||||
|
using std::swap;
|
||||||
|
if (this_global_x < other_global_x) {
|
||||||
|
swap(this_global_x, other_global_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this_global_y < other_global_y) {
|
||||||
|
swap(this_global_y, other_global_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t dx = this_global_x - other_global_x;
|
||||||
|
std::uint32_t dy = this_global_y - other_global_y;
|
||||||
|
return dx * dx + dy * dy;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<std::uint16_t, std::uint16_t> TilePos::to_global() const {
|
std::pair<std::uint16_t, std::uint16_t> TilePos::to_global() const {
|
||||||
return {chunk_x * Chunk::size + local_x, chunk_y * Chunk::size + local_y};
|
return {chunk_x * Chunk::size + local_x, chunk_y * Chunk::size + local_y};
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ void TerrainGenerator::operator()(TileMap &tilemap) {
|
|||||||
smoothen_islands_pass(tilemap);
|
smoothen_islands_pass(tilemap);
|
||||||
mountain_hole_fill_pass(tilemap);
|
mountain_hole_fill_pass(tilemap);
|
||||||
deepwater_pass(tilemap);
|
deepwater_pass(tilemap);
|
||||||
|
oil_pass(tilemap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_generate(TileMap &tilemap, const GenerationConfig &config) {
|
void map_generate(TileMap &tilemap, const GenerationConfig &config) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "noise.h"
|
#include "noise.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <bit>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <random>
|
#include <random>
|
||||||
@ -7,19 +8,23 @@
|
|||||||
|
|
||||||
namespace istd {
|
namespace istd {
|
||||||
|
|
||||||
DiscreteRandomNoise::DiscreteRandomNoise(Xoroshiro128PP rng) {
|
DiscreteRandomNoise::DiscreteRandomNoise(Xoroshiro128PP rng) noexcept {
|
||||||
mask = rng.next();
|
mask = rng.next();
|
||||||
std::iota(permutation_.begin(), permutation_.end(), 0);
|
std::iota(permutation_.begin(), permutation_.end(), 0);
|
||||||
std::shuffle(permutation_.begin(), permutation_.end(), rng);
|
std::shuffle(permutation_.begin(), permutation_.end(), rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint8_t DiscreteRandomNoise::perm(int x) const {
|
std::uint8_t DiscreteRandomNoise::perm(int x) const noexcept {
|
||||||
// Map x to [0, 255] range
|
// Map x to [0, 255] range
|
||||||
x &= 0xFF;
|
x &= 0xFF;
|
||||||
return permutation_[x];
|
return permutation_[x];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint32_t DiscreteRandomNoise::map(std::uint32_t x) const {
|
std::uint32_t DiscreteRandomNoise::rot8(std::uint32_t x) const noexcept {
|
||||||
|
return std::rotl(x, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t DiscreteRandomNoise::map_once(std::uint32_t x) const noexcept {
|
||||||
std::uint8_t a = x & 0xFF;
|
std::uint8_t a = x & 0xFF;
|
||||||
std::uint8_t b = (x >> 8) & 0xFF;
|
std::uint8_t b = (x >> 8) & 0xFF;
|
||||||
std::uint8_t c = (x >> 16) & 0xFF;
|
std::uint8_t c = (x >> 16) & 0xFF;
|
||||||
@ -31,9 +36,17 @@ std::uint32_t DiscreteRandomNoise::map(std::uint32_t x) const {
|
|||||||
return (d << 24U) | (c << 16U) | (b << 8U) | a;
|
return (d << 24U) | (c << 16U) | (b << 8U) | a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint32_t DiscreteRandomNoise::map(std::uint32_t x) const noexcept {
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
x = map_once(x);
|
||||||
|
x = rot8(x);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
std::uint64_t DiscreteRandomNoise::noise(
|
std::uint64_t DiscreteRandomNoise::noise(
|
||||||
std::uint32_t x, std::uint32_t y, std::uint32_t z
|
std::uint32_t x, std::uint32_t y, std::uint32_t z
|
||||||
) const {
|
) const noexcept {
|
||||||
auto A = map(x);
|
auto A = map(x);
|
||||||
auto B = map(y ^ A);
|
auto B = map(y ^ A);
|
||||||
auto C = map(z ^ B);
|
auto C = map(z ^ B);
|
||||||
@ -43,6 +56,22 @@ std::uint64_t DiscreteRandomNoise::noise(
|
|||||||
return ((static_cast<std::uint64_t>(C) << 32) | F) ^ mask;
|
return ((static_cast<std::uint64_t>(C) << 32) | F) ^ mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiscreteRandomNoiseStream::DiscreteRandomNoiseStream(
|
||||||
|
const DiscreteRandomNoise &noise, std::uint32_t x, std::uint32_t y,
|
||||||
|
std::uint32_t idx
|
||||||
|
)
|
||||||
|
: noise_(noise), x_(x), y_(y), idx_(idx) {}
|
||||||
|
|
||||||
|
std::uint64_t DiscreteRandomNoiseStream::next() noexcept {
|
||||||
|
std::uint64_t value = noise_.noise(x_, y_, idx_);
|
||||||
|
++idx_;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t DiscreteRandomNoiseStream::operator()() noexcept {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
PerlinNoise::PerlinNoise(Xoroshiro128PP rng) {
|
PerlinNoise::PerlinNoise(Xoroshiro128PP rng) {
|
||||||
// Initialize permutation array with values 0-255
|
// Initialize permutation array with values 0-255
|
||||||
permutation_.resize(256);
|
permutation_.resize(256);
|
||||||
@ -191,8 +220,9 @@ double UniformPerlinNoise::uniform_noise(double x, double y) const {
|
|||||||
|
|
||||||
double UniformPerlinNoise::map_to_uniform(double raw_value) const {
|
double UniformPerlinNoise::map_to_uniform(double raw_value) const {
|
||||||
// Find position in CDF using binary search
|
// Find position in CDF using binary search
|
||||||
auto it
|
auto it = std::lower_bound(
|
||||||
= std::lower_bound(cdf_values_.begin(), cdf_values_.end(), raw_value);
|
cdf_values_.begin(), cdf_values_.end(), raw_value
|
||||||
|
);
|
||||||
|
|
||||||
// Calculate quantile (position in CDF as fraction)
|
// Calculate quantile (position in CDF as fraction)
|
||||||
size_t position = std::distance(cdf_values_.begin(), it);
|
size_t position = std::distance(cdf_values_.begin(), it);
|
||||||
|
209
tilemap/src/pass/oil.cpp
Normal file
209
tilemap/src/pass/oil.cpp
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
#include "biome.h"
|
||||||
|
#include "chunk.h"
|
||||||
|
#include "generation.h"
|
||||||
|
#include "noise.h"
|
||||||
|
#include "xoroshiro.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <queue>
|
||||||
|
#include <random>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace istd {
|
||||||
|
|
||||||
|
OilGenerationPass::OilGenerationPass(
|
||||||
|
const GenerationConfig &config, Xoroshiro128PP rng, Xoroshiro128PP noise_rng
|
||||||
|
)
|
||||||
|
: config_(config), rng_(rng), noise_(noise_rng) {}
|
||||||
|
|
||||||
|
void OilGenerationPass::operator()(TileMap &tilemap) {
|
||||||
|
// Generate oil center positions using Poisson disk sampling
|
||||||
|
auto oil_centers = generate_oil_centers(tilemap);
|
||||||
|
|
||||||
|
// Generate oil clusters around each center
|
||||||
|
for (const auto ¢er : oil_centers) {
|
||||||
|
generate_oil_cluster(tilemap, center);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<TilePos> OilGenerationPass::generate_oil_centers(
|
||||||
|
const TileMap &tilemap
|
||||||
|
) {
|
||||||
|
std::vector<TilePos> centers;
|
||||||
|
std::uint8_t map_size = tilemap.get_size();
|
||||||
|
std::uint32_t total_chunks = map_size * map_size;
|
||||||
|
|
||||||
|
// Calculate expected number of oil fields based on density (out of 255)
|
||||||
|
std::uint32_t expected_oil_fields = (total_chunks * config_.oil_density)
|
||||||
|
/ 255;
|
||||||
|
|
||||||
|
// Minimum distance between oil fields to ensure spacing
|
||||||
|
std::uint32_t min_distance = calculate_min_oil_distance();
|
||||||
|
|
||||||
|
// Use Poisson disk sampling approach
|
||||||
|
const std::uint32_t max_coord = map_size * Chunk::size - 1;
|
||||||
|
|
||||||
|
// Generate candidates with rejection sampling
|
||||||
|
// Avoid infinite loops
|
||||||
|
std::uint32_t attempts = 0;
|
||||||
|
const std::uint32_t max_attempts = expected_oil_fields * 32;
|
||||||
|
std::uniform_int_distribution<std::uint16_t> dist(0, max_coord);
|
||||||
|
|
||||||
|
while (centers.size() < expected_oil_fields && attempts < max_attempts) {
|
||||||
|
++attempts;
|
||||||
|
|
||||||
|
// Generate random position using xoroshiro
|
||||||
|
const auto global_x = dist(rng_);
|
||||||
|
const auto global_y = dist(rng_);
|
||||||
|
|
||||||
|
TilePos candidate = TilePos::from_global(global_x, global_y);
|
||||||
|
|
||||||
|
// Check if position is suitable for oil
|
||||||
|
if (!is_suitable_for_oil(tilemap, candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check distance to existing oil centers
|
||||||
|
auto distance_checker = [candidate, min_distance](TilePos existing) {
|
||||||
|
return candidate.sqr_distance_to(existing)
|
||||||
|
< (min_distance * min_distance);
|
||||||
|
};
|
||||||
|
if (std::ranges::any_of(centers, distance_checker)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply biome preference
|
||||||
|
const Chunk &chunk = tilemap.get_chunk_of(candidate);
|
||||||
|
|
||||||
|
BiomeType biome = chunk.get_biome(candidate);
|
||||||
|
std::uint8_t biome_preference = get_biome_oil_preference(biome);
|
||||||
|
|
||||||
|
// Use integer probability check (0-255)
|
||||||
|
std::uint8_t sample = noise_.noise(global_x, global_y);
|
||||||
|
if (sample < biome_preference) {
|
||||||
|
centers.push_back(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return centers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OilGenerationPass::generate_oil_cluster(TileMap &tilemap, TilePos center) {
|
||||||
|
auto [global_x, global_y] = center.to_global();
|
||||||
|
|
||||||
|
auto span = config_.oil_cluster_max_size - config_.oil_cluster_min_size;
|
||||||
|
auto cluster_size = config_.oil_cluster_min_size;
|
||||||
|
// Binomial distribution for cluster size
|
||||||
|
for (int i = 1; i <= span; ++i) {
|
||||||
|
auto sample = noise_.noise(global_x, global_y, i) & 1;
|
||||||
|
cluster_size += sample; // Increase cluster size based on noise
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscreteRandomNoiseStream rng(noise_, global_x, global_y, 48);
|
||||||
|
|
||||||
|
std::vector<TilePos> cluster_tiles;
|
||||||
|
std::unordered_set<TilePos> visited;
|
||||||
|
|
||||||
|
// Start with center if suitable
|
||||||
|
cluster_tiles.push_back(center);
|
||||||
|
visited.insert(center);
|
||||||
|
|
||||||
|
// Grow cluster using random walk
|
||||||
|
std::queue<TilePos> candidates;
|
||||||
|
candidates.push(center);
|
||||||
|
|
||||||
|
while (!candidates.empty() && cluster_tiles.size() < cluster_size) {
|
||||||
|
TilePos current = candidates.front();
|
||||||
|
candidates.pop();
|
||||||
|
|
||||||
|
auto neighbors = tilemap.get_neighbors(current);
|
||||||
|
std::shuffle(neighbors.begin(), neighbors.end(), rng);
|
||||||
|
for (const auto neighbor : neighbors) {
|
||||||
|
// 50% chance to skip this neighbor
|
||||||
|
auto [neighbor_global_x, neighbor_global_y] = neighbor.to_global();
|
||||||
|
auto sample = noise_.noise(
|
||||||
|
neighbor_global_x, neighbor_global_y,
|
||||||
|
0x2b52aaed // random seed
|
||||||
|
);
|
||||||
|
if ((sample & 1) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited.count(neighbor) > 0) {
|
||||||
|
continue; // Already visited
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_suitable_for_oil(tilemap, neighbor)) {
|
||||||
|
continue; // Not suitable for oil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to cluster
|
||||||
|
cluster_tiles.push_back(neighbor);
|
||||||
|
visited.insert(neighbor);
|
||||||
|
|
||||||
|
// Stop if we reached the desired cluster size
|
||||||
|
if (cluster_tiles.size() >= cluster_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates.push(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place oil on all cluster tiles
|
||||||
|
for (const auto &pos : cluster_tiles) {
|
||||||
|
Tile &tile = tilemap.get_tile(pos);
|
||||||
|
tile.surface = SurfaceTileType::Oil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OilGenerationPass::is_suitable_for_oil(
|
||||||
|
const TileMap &tilemap, TilePos pos
|
||||||
|
) const {
|
||||||
|
const Tile &tile = tilemap.get_tile(pos);
|
||||||
|
|
||||||
|
// Oil can only be placed on land or sand, and surface must be empty
|
||||||
|
return (tile.base == BaseTileType::Land || tile.base == BaseTileType::Sand)
|
||||||
|
&& tile.surface == SurfaceTileType::Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t OilGenerationPass::get_biome_oil_preference(
|
||||||
|
BiomeType biome
|
||||||
|
) const {
|
||||||
|
std::uint8_t base_preference = config_.oil_base_probe;
|
||||||
|
|
||||||
|
switch (biome) {
|
||||||
|
case BiomeType::Desert:
|
||||||
|
case BiomeType::Plains:
|
||||||
|
return base_preference; // Full preference for desert/plains
|
||||||
|
case BiomeType::Savanna:
|
||||||
|
case BiomeType::SnowyPlains:
|
||||||
|
return (base_preference * 204) / 255; // ~80% preference
|
||||||
|
case BiomeType::Forest:
|
||||||
|
return (base_preference * 128) / 255; // ~50% preference
|
||||||
|
case BiomeType::SnowyPeeks:
|
||||||
|
return (base_preference * 77) / 255; // ~30% preference
|
||||||
|
case BiomeType::FrozenOcean:
|
||||||
|
case BiomeType::Ocean:
|
||||||
|
case BiomeType::LukeOcean:
|
||||||
|
default:
|
||||||
|
return 0; // No oil in oceans
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t OilGenerationPass::calculate_min_oil_distance() const {
|
||||||
|
// Base minimum distance on chunk size and oil density
|
||||||
|
// Lower density = higher minimum distance
|
||||||
|
std::uint32_t base_distance = Chunk::size * 4 / 5;
|
||||||
|
return base_distance * 255 / config_.oil_density;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerrainGenerator::oil_pass(TileMap &tilemap) {
|
||||||
|
auto rng = master_rng_;
|
||||||
|
master_rng_ = master_rng_.jump_96();
|
||||||
|
auto noise_rng = master_rng_;
|
||||||
|
master_rng_ = master_rng_.jump_96();
|
||||||
|
OilGenerationPass pass(config_, rng, noise_rng);
|
||||||
|
pass(tilemap);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace istd
|
@ -1,4 +1,5 @@
|
|||||||
#include "tilemap.h"
|
#include "tilemap.h"
|
||||||
|
#include "chunk.h"
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace istd {
|
namespace istd {
|
||||||
@ -31,6 +32,14 @@ const Chunk &TileMap::get_chunk(
|
|||||||
return chunks_[chunk_x][chunk_y];
|
return chunks_[chunk_x][chunk_y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Chunk &TileMap::get_chunk_of(TilePos pos) {
|
||||||
|
return get_chunk(pos.chunk_x, pos.chunk_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Chunk &TileMap::get_chunk_of(TilePos pos) const {
|
||||||
|
return get_chunk(pos.chunk_x, pos.chunk_y);
|
||||||
|
}
|
||||||
|
|
||||||
Tile &TileMap::get_tile(TilePos pos) {
|
Tile &TileMap::get_tile(TilePos pos) {
|
||||||
if (pos.chunk_x >= size_ || pos.chunk_y >= size_) {
|
if (pos.chunk_x >= size_ || pos.chunk_y >= size_) {
|
||||||
throw std::out_of_range("Chunk coordinates out of bounds");
|
throw std::out_of_range("Chunk coordinates out of bounds");
|
||||||
@ -68,7 +77,7 @@ bool TileMap::is_at_boundary(TilePos pos) const {
|
|||||||
std::uint32_t max_global = map_size * Chunk::size - 1;
|
std::uint32_t max_global = map_size * Chunk::size - 1;
|
||||||
|
|
||||||
return global_x == 0 || global_x == max_global || global_y == 0
|
return global_x == 0 || global_x == max_global || global_y == 0
|
||||||
|| global_y == max_global;
|
|| global_y == max_global;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TilePos> TileMap::get_neighbors(TilePos pos, bool chebyshiv) const {
|
std::vector<TilePos> TileMap::get_neighbors(TilePos pos, bool chebyshiv) const {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user