nil-core 0.5.4

Multiplayer strategy game
Documentation
// Copyright (C) Call of Nil contributors
// SPDX-License-Identifier: AGPL-3.0-only

pub mod stability;

use crate::city::stability::Stability;
use crate::continent::Coord;
use crate::error::Result;
use crate::infrastructure::Infrastructure;
use crate::infrastructure::building::StorageId;
use crate::infrastructure::stats::InfrastructureStats;
use crate::infrastructure::storage::OverallStorageCapacity;
use crate::npc::bot::BotId;
use crate::npc::precursor::PrecursorId;
use crate::player::PlayerId;
use crate::ranking::score::Score;
use crate::resources::Resources;
use crate::resources::maintenance::Maintenance;
use crate::ruler::Ruler;
use bon::Builder;
use derive_more::{Deref, DerefMut, From};
use serde::{Deserialize, Serialize};
use std::sync::Arc;

#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct City {
  #[builder(start_fn, into)]
  coord: Coord,

  #[builder(into)]
  name: CityName,

  #[builder(into)]
  owner: Ruler,

  #[builder(default)]
  infrastructure: Infrastructure,

  #[builder(default)]
  stability: Stability,
}

impl City {
  #[inline]
  pub fn coord(&self) -> Coord {
    self.coord
  }

  #[inline]
  pub fn name(&self) -> &CityName {
    &self.name
  }

  pub(crate) fn name_mut(&mut self) -> &mut CityName {
    &mut self.name
  }

  #[inline]
  pub fn owner(&self) -> &Ruler {
    &self.owner
  }

  #[inline]
  pub fn infrastructure(&self) -> &Infrastructure {
    &self.infrastructure
  }

  #[inline]
  pub fn infrastructure_mut(&mut self) -> &mut Infrastructure {
    &mut self.infrastructure
  }

  #[inline]
  pub fn stability(&self) -> Stability {
    self.stability
  }

  pub(crate) fn stability_mut(&mut self) -> &mut Stability {
    &mut self.stability
  }

  #[inline]
  pub fn player(&self) -> Option<PlayerId> {
    self.owner().player().cloned()
  }

  /// Checks whether the city belongs to a player.
  #[inline]
  pub fn is_owned_by_player(&self) -> bool {
    self.owner.player().is_some()
  }

  pub fn is_owned_by_player_and<F>(&self, f: F) -> bool
  where
    F: FnOnce(&PlayerId) -> bool,
  {
    self.owner.player().is_some_and(f)
  }

  /// Checks whether the city belongs to a bot.
  #[inline]
  pub fn is_owned_by_bot(&self) -> bool {
    self.owner.bot().is_some()
  }

  pub fn is_owned_by_bot_and<F>(&self, f: F) -> bool
  where
    F: FnOnce(&BotId) -> bool,
  {
    self.owner.bot().is_some_and(f)
  }

  /// Checks whether the city belongs to a precursor.
  #[inline]
  pub fn is_owned_by_precursor(&self) -> bool {
    self.owner.precursor().is_some()
  }

  pub fn is_owned_by_precursor_and<F>(&self, f: F) -> bool
  where
    F: FnOnce(PrecursorId) -> bool,
  {
    self.owner.precursor().is_some_and(f)
  }

  #[inline]
  pub fn score(&self, stats: &InfrastructureStats) -> Result<Score> {
    self.infrastructure.score(stats)
  }

  /// Determines the amount of resources generated by the city's mines
  /// while applying all relevant modifiers, such as city stability.
  pub fn round_production(&self, stats: &InfrastructureStats) -> Result<Resources> {
    let mut resources = self
      .infrastructure
      .round_base_production(stats)?;

    resources.food *= self.stability;
    resources.iron *= self.stability;
    resources.stone *= self.stability;
    resources.wood *= self.stability;

    Ok(resources)
  }

  /// Determines the maintenance tax required for the city buildings.
  pub fn maintenance(&self, stats: &InfrastructureStats) -> Result<Maintenance> {
    self.infrastructure.base_maintenance(stats)
  }

  pub fn storage_capacity(&self, stats: &InfrastructureStats) -> Result<OverallStorageCapacity> {
    let silo_stats = stats.storage(StorageId::Silo)?;
    let warehouse_stats = stats.storage(StorageId::Warehouse)?;

    Ok(OverallStorageCapacity {
      silo: self
        .infrastructure
        .storage(StorageId::Silo)
        .capacity(silo_stats)?,
      warehouse: self
        .infrastructure
        .storage(StorageId::Warehouse)
        .capacity(warehouse_stats)?,
    })
  }
}

impl From<&City> for Ruler {
  fn from(city: &City) -> Self {
    city.owner.clone()
  }
}

#[derive(Clone, Debug, Deref, DerefMut, From, Deserialize, Serialize)]
#[from(String, &str)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct CityName(String);

impl CityName {
  pub fn new(name: impl AsRef<str>) -> Self {
    Self(name.as_ref().to_owned())
  }
}

/// Public data about a city, to which any player can have access.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct PublicCity {
  coord: Coord,
  name: Arc<str>,
  owner: Ruler,
}

impl From<&City> for PublicCity {
  fn from(city: &City) -> Self {
    Self {
      coord: city.coord,
      name: Arc::from(city.name.as_str()),
      owner: city.owner.clone(),
    }
  }
}

#[derive(Builder, Clone, Debug, Default, Deserialize, Serialize)]
#[serde(default, rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[cfg_attr(feature = "typescript", ts(optional_fields))]
pub struct CitySearch {
  #[builder(default, with = FromIterator::from_iter)]
  #[cfg_attr(feature = "typescript", ts(as = "Option<Vec<Coord>>"))]
  pub coord: Vec<Coord>,

  #[builder(default, with = FromIterator::from_iter)]
  #[cfg_attr(feature = "typescript", ts(as = "Option<Vec<CityName>>"))]
  pub name: Vec<CityName>,
}

impl From<Coord> for CitySearch {
  fn from(coord: Coord) -> Self {
    Self::from_iter([coord])
  }
}

impl From<CityName> for CitySearch {
  fn from(name: CityName) -> Self {
    Self::from_iter([name])
  }
}

impl FromIterator<Coord> for CitySearch {
  fn from_iter<T>(iter: T) -> Self
  where
    T: IntoIterator<Item = Coord>,
  {
    Self::builder().coord(iter).build()
  }
}

impl FromIterator<CityName> for CitySearch {
  fn from_iter<T>(iter: T) -> Self
  where
    T: IntoIterator<Item = CityName>,
  {
    Self::builder().name(iter).build()
  }
}