pub mod building;
pub mod catalog;
pub mod mine;
pub mod prelude;
pub mod queue;
pub mod requirements;
pub mod stats;
pub mod storage;
use crate::error::Result;
use crate::military::army::personnel::ArmyPersonnel;
use crate::military::squad::Squad;
use crate::ranking::score::Score;
use crate::resources::Resources;
use crate::resources::maintenance::Maintenance;
use crate::world::config::WorldConfig;
use bon::Builder;
use building::r#impl::academy::recruit_queue::{
AcademyRecruitOrder,
AcademyRecruitOrderId,
AcademyRecruitOrderRequest,
};
use building::r#impl::prefecture::build_queue::{
PrefectureBuildOrder,
PrefectureBuildOrderKind,
PrefectureBuildOrderRequest,
};
use building::r#impl::stable::recruit_queue::{
StableRecruitOrder,
StableRecruitOrderId,
StableRecruitOrderRequest,
};
use building::r#impl::workshop::recruit_queue::{
WorkshopRecruitOrder,
WorkshopRecruitOrderId,
WorkshopRecruitOrderRequest,
};
use building::{Building, BuildingId, BuildingStatsTable, MineId, StorageId};
use mine::Mine;
use prelude::*;
use requirements::InfrastructureRequirements;
use serde::{Deserialize, Serialize};
use stats::InfrastructureStats;
use storage::Storage;
use strum::IntoEnumIterator;
use tap::Pipe;
#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct Infrastructure {
#[builder(default)]
academy: Academy,
#[builder(default)]
farm: Farm,
#[builder(default)]
iron_mine: IronMine,
#[builder(default)]
prefecture: Prefecture,
#[builder(default)]
quarry: Quarry,
#[builder(default)]
sawmill: Sawmill,
#[builder(default)]
silo: Silo,
#[builder(default)]
stable: Stable,
#[builder(default)]
wall: Wall,
#[builder(default)]
warehouse: Warehouse,
#[builder(default)]
workshop: Workshop,
}
impl Infrastructure {
#[inline]
pub fn new() -> Self {
Self::default()
}
pub const fn storage(&self, id: StorageId) -> &dyn Storage {
match id {
StorageId::Silo => &self.silo,
StorageId::Warehouse => &self.warehouse,
}
}
pub const fn mine(&self, id: MineId) -> &dyn Mine {
match id {
MineId::Farm => &self.farm,
MineId::IronMine => &self.iron_mine,
MineId::Quarry => &self.quarry,
MineId::Sawmill => &self.sawmill,
}
}
pub fn score(&self, stats: &InfrastructureStats) -> Result<Score> {
let mut score = Score::default();
for id in BuildingId::iter() {
let level = self.building(id).level();
if level > 0u8 {
let stats = stats.building(id)?;
score += stats.get(level)?.score;
}
}
Ok(score)
}
pub fn round_base_production(&self, stats: &InfrastructureStats) -> Result<Resources> {
let mut resources = Resources::default();
macro_rules! set {
($building:ident, $resource:ident) => {
paste::paste! {
let mine = &self.[<$building:snake>];
if mine.level() > 0u8 && mine.is_enabled() {
let mine_stats = stats.mine(MineId::$building)?;
resources.$resource = mine.production(mine_stats)?.into();
}
}
};
}
set!(Farm, food);
set!(IronMine, iron);
set!(Quarry, stone);
set!(Sawmill, wood);
Ok(resources)
}
pub(crate) fn add_prefecture_build_order(
&mut self,
request: &PrefectureBuildOrderRequest,
table: &BuildingStatsTable,
current_resources: Option<&Resources>,
) -> Result<&PrefectureBuildOrder> {
let level = self.building(request.building).level();
self
.prefecture
.build_queue_mut()
.build(request, table, level, current_resources)
}
#[must_use]
pub(crate) fn cancel_prefecture_build_order(&mut self) -> Option<PrefectureBuildOrder> {
self.prefecture.build_queue_mut().cancel()
}
pub(crate) fn process_prefecture_build_queue(&mut self, config: &WorldConfig) {
if let Some(orders) = self.prefecture.process_queue(config) {
for order in orders {
let building = self.building_mut(order.building());
match order.kind() {
PrefectureBuildOrderKind::Construction => building.increase_level(),
PrefectureBuildOrderKind::Demolition => building.decrease_level(),
}
}
}
}
}
macro_rules! impl_infrastructure {
($($building:ident),+) => {
paste::paste! {
impl Infrastructure {
$(
#[inline]
pub const fn [<$building:snake>](&self) -> &$building {
&self.[<$building:snake>]
}
)+
pub fn with_max_level() -> Self {
Self {
$([<$building:snake>]: $building::with_max_level(),)+
}
}
pub const fn building(&self, id: BuildingId) -> &dyn Building {
match id {
$(BuildingId::$building => &self.[<$building:snake>],)+
}
}
pub(crate) const fn building_mut(&mut self, id: BuildingId) -> &mut dyn Building {
match id {
$(BuildingId::$building => &mut self.[<$building:snake>],)+
}
}
pub fn base_maintenance(&self, stats: &InfrastructureStats) -> Result<Maintenance> {
let mut maintenance = Maintenance::default();
$(
let building = &self.[<$building:snake>];
if building.level() > 0u8 && building.is_enabled() {
let building_stats = stats.building(BuildingId::$building)?;
maintenance += building.maintenance(&building_stats)?;
}
)+
Ok(maintenance)
}
pub fn has_required_levels(&self, requirements: &InfrastructureRequirements) -> bool {
$(self.[<$building:snake>].level() >= requirements.[<$building:snake>] &&)+ true
}
}
}
};
}
impl_infrastructure!(
Academy, Farm, IronMine, Prefecture, Quarry, Sawmill, Silo, Stable, Wall, Warehouse, Workshop
);
macro_rules! impl_recruitment {
($building:ident) => {
paste::paste! {
impl Infrastructure {
pub(crate) fn [<add_ $building:snake _recruit_order>](
&mut self,
request: &[<$building RecruitOrderRequest>],
current_resources: Option<&Resources>,
) -> Result<&[<$building RecruitOrder>]> {
self
.[<$building:snake>]
.recruit_queue_mut()
.recruit(request, current_resources)
}
#[must_use]
pub(crate) fn [<cancel_ $building:snake _recruit_order>](
&mut self,
id: [<$building RecruitOrderId>]
) -> Option<[<$building RecruitOrder>]> {
self.[<$building:snake>].recruit_queue_mut().cancel(id)
}
#[must_use]
pub(crate) fn [<process_ $building:snake _recruit_queue>](
&mut self,
config: &WorldConfig
) -> Option<ArmyPersonnel> {
self
.[<$building:snake>]
.process_queue(config)?
.into_iter()
.map(Squad::from)
.collect::<ArmyPersonnel>()
.pipe(Some)
}
}
}
};
}
impl_recruitment!(Academy);
impl_recruitment!(Stable);
impl_recruitment!(Workshop);