pub mod personnel;
use crate::continent::ContinentKey;
use crate::military::Military;
use crate::military::maneuver::ManeuverId;
use crate::military::squad::Squad;
use crate::military::unit::UnitId;
use crate::military::unit::stats::haul::Haul;
use crate::military::unit::stats::power::{AttackPower, DefensePower, Power};
use crate::military::unit::stats::speed::Speed;
use crate::ranking::score::Score;
use crate::resources::maintenance::Maintenance;
use crate::ruler::Ruler;
use crate::world::config::WorldConfig;
use bon::Builder;
use derive_more::Display;
use personnel::{ArmyPersonnel, ArmyPersonnelIter};
use serde::{Deserialize, Serialize};
use std::mem;
use std::ops::{Mul, MulAssign};
use strum::{EnumIs, IntoEnumIterator};
use uuid::Uuid;
#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
#[builder(builder_type(vis = "pub(crate)"))]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct Army {
#[builder(skip)]
id: ArmyId,
#[builder(default)]
personnel: ArmyPersonnel,
#[builder(into)]
owner: Ruler,
#[builder(default, into)]
state: ArmyState,
}
impl Army {
#[inline]
pub fn id(&self) -> ArmyId {
self.id
}
#[inline]
pub fn personnel(&self) -> &ArmyPersonnel {
&self.personnel
}
pub(crate) fn personnel_mut(&mut self) -> &mut ArmyPersonnel {
&mut self.personnel
}
#[inline]
pub fn iter(&self) -> ArmyPersonnelIter<'_> {
self.personnel.iter()
}
#[inline]
pub fn squad(&self, id: UnitId) -> &Squad {
self.personnel.squad(id)
}
fn squad_mut(&mut self, id: UnitId) -> &mut Squad {
self.personnel.squad_mut(id)
}
#[inline]
pub fn owner(&self) -> &Ruler {
&self.owner
}
#[inline]
pub fn state(&self) -> &ArmyState {
&self.state
}
pub(crate) fn state_mut(&mut self) -> &mut ArmyState {
&mut self.state
}
#[inline]
pub fn slowest_squad(&self, config: &WorldConfig) -> Option<&Squad> {
self.personnel.slowest_squad(config)
}
#[inline]
pub fn speed(&self, config: &WorldConfig) -> Speed {
self.personnel.speed(config)
}
#[inline]
pub fn haul(&self) -> Haul {
self.personnel.haul()
}
#[inline]
pub fn score(&self) -> Score {
self.personnel.score()
}
#[inline]
pub fn maintenance(&self) -> Maintenance {
self.personnel.maintenance()
}
#[inline]
pub fn power(&self) -> Power {
self.personnel.power()
}
#[inline]
pub fn attack(&self) -> AttackPower {
self.personnel.attack()
}
#[inline]
pub fn defense(&self) -> DefensePower {
self.personnel.defense()
}
#[inline]
pub fn is_owned_by(&self, ruler: &Ruler) -> bool {
self.owner.eq(ruler)
}
#[inline]
pub fn is_idle_and_owned_by(&self, ruler: &Ruler) -> bool {
self.is_idle() && self.is_owned_by(ruler)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.personnel.is_empty()
}
#[inline]
pub fn is_idle(&self) -> bool {
self.state.is_idle()
}
#[inline]
pub fn is_maneuvering(&self) -> bool {
self.state.is_maneuvering()
}
#[inline]
pub fn has_enough_personnel(&self, required: &ArmyPersonnel) -> bool {
self.personnel.has_enough_personnel(required)
}
pub(crate) fn spawn<K>(self, military: &mut Military, key: K)
where
K: ContinentKey,
{
military.spawn(key, self.owner, self.personnel);
}
}
macro_rules! impl_army {
($($unit:ident),+) => {
paste::paste! {
impl Army {
$(
#[inline]
pub fn [<$unit:snake>](&self) -> &Squad {
&self.personnel.[<$unit:snake>]()
}
)+
}
}
};
}
impl_army!(
Archer,
Axeman,
HeavyCavalry,
LightCavalry,
Pikeman,
Ram,
Swordsman
);
impl<'a> IntoIterator for &'a Army {
type Item = &'a Squad;
type IntoIter = ArmyPersonnelIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Mul<f64> for Army {
type Output = Army;
fn mul(mut self, rhs: f64) -> Self::Output {
self *= rhs;
self
}
}
impl MulAssign<f64> for Army {
fn mul_assign(&mut self, rhs: f64) {
for id in UnitId::iter() {
*self.squad_mut(id) *= rhs;
}
}
}
#[must_use]
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Deserialize, Serialize)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct ArmyId(Uuid);
impl ArmyId {
#[inline]
pub fn new() -> Self {
Self(Uuid::new_v4())
}
}
impl Default for ArmyId {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, EnumIs)]
#[serde(tag = "kind", rename_all = "kebab-case")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub enum ArmyState {
#[default]
Idle,
Maneuvering {
maneuver: ManeuverId,
},
}
impl ArmyState {
#[inline]
pub fn with_maneuver(maneuver: ManeuverId) -> Self {
Self::Maneuvering { maneuver }
}
}
pub fn collapse_armies(armies: &mut Vec<Army>) {
for army in mem::take(armies)
.into_iter()
.filter(|army| !army.is_empty())
{
if army.is_idle()
&& let Some(previous) = find_idle_owned_by(armies, army.owner())
{
*previous.personnel_mut() += ArmyPersonnel::from(army);
} else {
armies.push(army);
}
}
}
fn find_idle_owned_by<'a>(armies: &'a mut [Army], ruler: &Ruler) -> Option<&'a mut Army> {
armies
.iter_mut()
.find(|army| army.is_idle_and_owned_by(ruler))
}