1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use rand::Rng;
use crate::{
tile::Tile,
tile_component::{BaseTerrain, TerrainType},
tile_map::{MapParameters, TileMap, impls::generate_area_and_landmass::LandmassType},
};
impl TileMap {
/// Generate [`BaseTerrain::Lake`] on the map.
///
/// This function is used because when we create the map by [`TileMap::generate_terrain_types`], some water areas will be created surrounded by land.
/// If these water areas are small enough, they will be considered as lakes and will be replaced by [`BaseTerrain::Lake`].
pub fn generate_lakes(&mut self, map_parameters: &MapParameters) {
self.all_tiles().for_each(|tile| {
let landmass_id = tile.landmass_id(self);
if self.landmass_list[landmass_id].landmass_type == LandmassType::Water
&& self.landmass_list[landmass_id].size <= map_parameters.max_lake_area_size
{
tile.set_base_terrain(self, BaseTerrain::Lake);
}
});
}
/// Add lakes to the map.
///
/// Besides the lakes generated by [`TileMap::generate_lakes`], this function will add more lakes to the map.
pub fn add_lakes(&mut self, map_parameters: &MapParameters) {
let num_large_lake = map_parameters.num_large_lakes;
// TODO: `lake_tile_rand` should be configurable by the user in the future.
// That means that when the tile is eligible for adding a lake, we will randomly decide whether to add a lake to the tile based on `lake_tile_rand`. Larger `lake_tile_rand`, less likely to add a lake.
// `lake_tile_rand` is set to 25, which means that when the tile is eligible for adding a lake, there is a 1/25 chance to add a lake to the tile.
let lake_tile_rand = 25;
let mut num_large_lakes_added = 0;
self.all_tiles().for_each(|tile| {
if self.can_add_lake(tile)
&& self.random_number_generator.random_range(0..lake_tile_rand) == 0
{
if num_large_lakes_added < num_large_lake {
let add_more_lakes = self.add_more_lake(tile);
if add_more_lakes {
num_large_lakes_added += 1;
}
}
tile.set_terrain_type(self, TerrainType::Water);
tile.set_base_terrain(self, BaseTerrain::Lake);
tile.clear_feature(self);
}
});
}
/// Transform the neighboring tiles of the given tile into lakes if possible.
///
/// # Notice
///
/// This function is only used in CIV6.
fn add_more_lake(&mut self, tile: Tile) -> bool {
let grid = self.world_grid.grid;
let mut large_lake = 0;
let mut lake_tiles = Vec::new();
// Get the candidate tiles for the lake.
// Notice: Don't transform the candidate tiles into lakes in this loop,
// because if we do so, the result will not be as expected.
tile.neighbor_tiles(grid).for_each(|neighbor_tile| {
// 1. Check if the tile can have a lake.
// 2. Randomly decide whether to add a lake to the tile. Larger `large_lake`, less likely to add a lake.
if self.can_add_lake(neighbor_tile)
&& self
.random_number_generator
.random_range(0..(large_lake + 4))
< 3
{
lake_tiles.push(neighbor_tile);
large_lake += 1;
}
});
lake_tiles.into_iter().for_each(|tile| {
tile.set_terrain_type(self, TerrainType::Water);
tile.set_base_terrain(self, BaseTerrain::Lake);
tile.clear_feature(self);
});
large_lake > 2
}
/// Checks if a tile can have a lake.
///
/// A tile can have a lake if it meets all of the following conditions:
/// 1. It is not water.
/// 2. It is not a natural wonder.
/// 3. It is not adjacent to a river.
/// 4. It is not adjacent to water.
/// 5. It is not adjacent to a natural wonder.
///
/// # Arguments
///
/// - `tile`: The tile being checked.
///
/// # Returns
///
/// Returns `true` if the tile can have a lake, otherwise `false`.
fn can_add_lake(&self, tile: Tile) -> bool {
let grid = self.world_grid.grid;
// Check if the current tile is suitable for a lake
if tile.terrain_type(self) == TerrainType::Water
|| tile.natural_wonder(self).is_some()
|| tile.has_river(self)
{
return false;
}
// Check if all neighbor tiles are also suitable
tile.neighbor_tiles(grid).all(|neighbor_tile| {
neighbor_tile.terrain_type(self) != TerrainType::Water
&& neighbor_tile.natural_wonder(self).is_none()
})
}
}