use core::debug_assert;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::grid::{
GridSize, Rectangle, Size, WorldSizeType, WrapFlags,
hex_grid::{HexGrid, HexLayout, HexOrientation, Offset},
};
pub struct MapParameters {
pub seed: u64,
pub map_type: MapType,
pub world_grid: WorldGrid,
pub world_size_type_profile: WorldSizeTypeProfile,
pub num_large_lakes: u32,
pub max_lake_area_size: u32,
pub coast_expand_chance: Vec<f64>,
pub sea_level: SeaLevel,
pub world_age: WorldAge,
pub temperature: Temperature,
pub rainfall: Rainfall,
pub region_divide_method: RegionDivideMethod,
pub civ_require_coastal_land_start: bool,
pub resource_setting: ResourceSetting,
}
impl MapParameters {
pub const MAX_CIVILIZATION_NUM: u32 = 22;
pub const MAX_CITY_STATE_NUM: u32 = 41;
pub const MAX_REGIONS_PER_EXCLUSIVE_LUXURY_TYPE: u32 = 3;
pub const NUM_MAX_ALLOWED_LUXURY_TYPES_FOR_REGIONS: usize =
Self::MAX_CIVILIZATION_NUM.div_ceil(Self::MAX_REGIONS_PER_EXCLUSIVE_LUXURY_TYPE) as usize;
pub const NUM_MAX_ALLOWED_LUXURY_TYPES_FOR_CITY_STATES: usize = 3;
}
pub struct MapParametersBuilder {
seed: u64,
world_grid: WorldGrid,
map_type: MapType,
world_size_type_profile: WorldSizeTypeProfile,
num_large_lakes: u32,
max_lake_area_size: u32,
coast_expand_chance: Vec<f64>,
sea_level: SeaLevel,
world_age: WorldAge,
temperature: Temperature,
rainfall: Rainfall,
region_divide_method: RegionDivideMethod,
civ_require_coastal_land_start: bool,
resource_setting: ResourceSetting,
}
impl MapParametersBuilder {
pub fn new(world_grid: WorldGrid) -> Self {
Self {
seed: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis()
.try_into()
.unwrap(),
world_grid,
map_type: Default::default(),
world_size_type_profile: WorldSizeTypeProfile::from_world_size_type(
world_grid.world_size(),
),
num_large_lakes: 2,
max_lake_area_size: 9,
coast_expand_chance: vec![0.25, 0.25], sea_level: SeaLevel::Normal,
world_age: WorldAge::Normal,
temperature: Temperature::Normal,
rainfall: Rainfall::Normal,
region_divide_method: RegionDivideMethod::Continent,
civ_require_coastal_land_start: false,
resource_setting: ResourceSetting::Standard,
}
}
pub fn seed(mut self, seed: u64) -> Self {
self.seed = seed;
self
}
pub fn map_type(mut self, map_type: MapType) -> Self {
self.map_type = map_type;
self
}
pub fn world_size_type_profile(mut self, profile: WorldSizeTypeProfile) -> Self {
self.world_size_type_profile = profile;
self
}
pub fn num_large_lakes(mut self, count: u32) -> Self {
self.num_large_lakes = count;
self
}
pub fn max_lake_area_size(mut self, size: u32) -> Self {
self.max_lake_area_size = size;
self
}
pub fn coast_expand_chance(mut self, chances: Vec<f64>) -> Self {
self.coast_expand_chance = chances;
self
}
pub fn sea_level(mut self, sea_level: SeaLevel) -> Self {
self.sea_level = sea_level;
self
}
pub fn world_age(mut self, age: WorldAge) -> Self {
self.world_age = age;
self
}
pub fn temperature(mut self, temperature: Temperature) -> Self {
self.temperature = temperature;
self
}
pub fn rainfall(mut self, rainfall: Rainfall) -> Self {
self.rainfall = rainfall;
self
}
pub fn region_divide_method(mut self, method: RegionDivideMethod) -> Self {
self.region_divide_method = method;
self
}
pub fn civ_require_coastal_land_start(mut self, require: bool) -> Self {
self.civ_require_coastal_land_start = require;
self
}
pub fn resource_setting(mut self, setting: ResourceSetting) -> Self {
self.resource_setting = setting;
self
}
pub fn build(self) -> MapParameters {
MapParameters {
map_type: self.map_type,
world_grid: self.world_grid,
seed: self.seed,
world_size_type_profile: self.world_size_type_profile,
num_large_lakes: self.num_large_lakes,
max_lake_area_size: self.max_lake_area_size,
coast_expand_chance: self.coast_expand_chance,
sea_level: self.sea_level,
world_age: self.world_age,
temperature: self.temperature,
rainfall: self.rainfall,
region_divide_method: self.region_divide_method,
civ_require_coastal_land_start: self.civ_require_coastal_land_start,
resource_setting: self.resource_setting,
}
}
}
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct WorldGrid {
pub grid: HexGrid,
pub world_size_type: WorldSizeType,
}
impl WorldGrid {
pub fn new(grid: HexGrid, world_size: WorldSizeType) -> Self {
debug_assert!(
grid.world_size_type() == world_size,
"Grid size does not match the specified world size"
);
Self {
grid,
world_size_type: world_size,
}
}
pub fn from_grid(grid: HexGrid) -> Self {
let world_size = grid.world_size_type();
Self {
grid,
world_size_type: world_size,
}
}
pub fn size(&self) -> Size {
self.grid.size
}
pub fn world_size(&self) -> WorldSizeType {
self.world_size_type
}
}
impl Default for WorldGrid {
fn default() -> Self {
let world_size = WorldSizeType::Standard;
let grid = HexGrid {
size: HexGrid::default_size(world_size),
layout: HexLayout {
orientation: HexOrientation::Pointy,
size: [50., 50.],
origin: [0., 0.],
},
wrap_flags: WrapFlags::WrapX,
offset: Offset::Odd,
};
Self {
grid,
world_size_type: world_size,
}
}
}
#[derive(Default)]
pub enum MapType {
#[default]
Fractal,
Pangaea,
}
#[derive(Default)]
pub enum SeaLevel {
Low,
#[default]
Normal,
High,
Random,
}
#[derive(Default)]
pub enum WorldAge {
Old,
#[default]
Normal,
New,
}
#[derive(Default)]
pub enum Temperature {
Cool,
#[default]
Normal,
Hot,
}
#[derive(Default)]
pub enum Rainfall {
Arid,
#[default]
Normal,
Wet,
Random,
}
#[derive(Default)]
pub enum RegionDivideMethod {
Pangaea,
#[default]
Continent,
WholeMapRectangle,
CustomRectangle(Rectangle),
}
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum ResourceSetting {
Sparse,
#[default]
Standard,
Abundant,
LegendaryStart,
StrategicBalance,
}
pub struct WorldSizeTypeProfile {
pub num_civilizations: u32,
pub num_city_states: u32,
pub num_natural_wonders: u32,
pub max_religions: u32,
pub unhappiness_per_city: f32,
pub unhappiness_annexed: f32,
pub tech_cost_base: f32,
pub tech_cost_per_city: f32,
pub policy_cost_per_city: f32,
}
impl WorldSizeTypeProfile {
#[allow(clippy::too_many_arguments)]
pub fn new(
num_civilizations: u32,
num_city_states: u32,
num_natural_wonders: u32,
max_religions: u32,
unhappiness_per_city: f32,
unhappiness_annexed: f32,
tech_cost_base: f32,
tech_cost_per_city: f32,
policy_cost_per_city: f32,
) -> Self {
Self {
num_civilizations,
num_city_states,
num_natural_wonders,
max_religions,
unhappiness_per_city,
unhappiness_annexed,
tech_cost_base,
tech_cost_per_city,
policy_cost_per_city,
}
}
pub fn from_world_size_type(world_size_type: WorldSizeType) -> Self {
match world_size_type {
WorldSizeType::Duel => Self::new(2, 4, 2, 2, 3.0, 5.0, 1.0, 0.05, 0.10),
WorldSizeType::Tiny => Self::new(4, 8, 3, 3, 3.0, 5.0, 1.0, 0.05, 0.10),
WorldSizeType::Small => Self::new(6, 12, 4, 4, 3.0, 5.0, 1.0, 0.05, 0.10),
WorldSizeType::Standard => Self::new(8, 16, 5, 5, 3.0, 5.0, 1.1, 0.05, 0.10),
WorldSizeType::Large => Self::new(10, 20, 6, 6, 2.4, 4.0, 1.2, 0.03, 0.075),
WorldSizeType::Huge => Self::new(12, 24, 7, 7, 1.8, 3.0, 1.3, 0.02, 0.05),
}
}
}