Skip to main content

nil_core/infrastructure/building/prefecture/
build_queue.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::continent::Coord;
5use crate::error::{Error, Result};
6use crate::infrastructure::building::{BuildingId, BuildingLevel, BuildingStatsTable};
7use crate::infrastructure::queue::{InfrastructureQueue, InfrastructureQueueOrder};
8use crate::resources::Resources;
9use crate::resources::workforce::Workforce;
10use nil_num::ops::MulCeil;
11use serde::{Deserialize, Serialize};
12use std::collections::VecDeque;
13use strum::EnumIs;
14use uuid::Uuid;
15
16#[derive(Clone, Debug, Default, Deserialize, Serialize)]
17#[serde(rename_all = "camelCase")]
18pub struct PrefectureBuildQueue {
19  orders: VecDeque<PrefectureBuildOrder>,
20}
21
22impl PrefectureBuildQueue {
23  pub(crate) fn build(
24    &mut self,
25    request: &PrefectureBuildOrderRequest,
26    table: &BuildingStatsTable,
27    current_level: BuildingLevel,
28    current_resources: Option<&Resources>,
29  ) -> Result<&PrefectureBuildOrder> {
30    let id = table.id();
31    let mut target_level = self.resolve_level(id, current_level);
32
33    let kind = request.kind;
34    if kind.is_demolition() && target_level <= table.min_level() {
35      return Err(Error::CannotDecreaseBuildingLevel(id));
36    } else if kind.is_construction() && target_level >= table.max_level() {
37      return Err(Error::CannotIncreaseBuildingLevel(id));
38    }
39
40    target_level += match kind {
41      PrefectureBuildOrderKind::Construction => 1i8,
42      PrefectureBuildOrderKind::Demolition => -1i8,
43    };
44
45    let resources = table.get(target_level)?.resources.clone();
46    if let PrefectureBuildOrderKind::Construction = kind
47      && let Some(current_resources) = current_resources
48      && current_resources
49        .checked_sub(&resources)
50        .is_none()
51    {
52      return Err(Error::InsufficientResources);
53    }
54
55    let mut workforce = table.get(target_level)?.workforce;
56    kind.apply_modifier(&mut workforce);
57
58    self.orders.push_back(PrefectureBuildOrder {
59      id: PrefectureBuildOrderId::new(),
60      kind,
61      building: id,
62      level: target_level,
63      resources,
64      workforce,
65      state: PrefectureBuildOrderState::new(workforce),
66    });
67
68    let len = self.orders.len();
69    Ok(unsafe {
70      self
71        .orders
72        .get(len.unchecked_sub(1))
73        .unwrap_unchecked()
74    })
75  }
76
77  /// Cancels the last build order in the queue.
78  #[must_use]
79  pub(crate) fn cancel(&mut self) -> Option<PrefectureBuildOrder> {
80    self.orders.pop_back()
81  }
82
83  pub fn resolve_level(&self, building: BuildingId, current_level: BuildingLevel) -> BuildingLevel {
84    self
85      .iter()
86      .filter(|order| order.building() == building)
87      .fold(current_level, |acc, order| {
88        match order.kind() {
89          PrefectureBuildOrderKind::Construction => acc + 1u8,
90          PrefectureBuildOrderKind::Demolition => acc - 1u8,
91        }
92      })
93  }
94}
95
96impl InfrastructureQueue<PrefectureBuildOrder> for PrefectureBuildQueue {
97  fn queue(&self) -> &VecDeque<PrefectureBuildOrder> {
98    &self.orders
99  }
100
101  fn queue_mut(&mut self) -> &mut VecDeque<PrefectureBuildOrder> {
102    &mut self.orders
103  }
104}
105
106#[must_use]
107#[derive(Clone, Debug, Deserialize, Serialize)]
108#[serde(rename_all = "camelCase")]
109pub struct PrefectureBuildOrder {
110  id: PrefectureBuildOrderId,
111  kind: PrefectureBuildOrderKind,
112  building: BuildingId,
113  level: BuildingLevel,
114  resources: Resources,
115  workforce: Workforce,
116  state: PrefectureBuildOrderState,
117}
118
119impl PrefectureBuildOrder {
120  #[inline]
121  pub fn id(&self) -> PrefectureBuildOrderId {
122    self.id
123  }
124
125  #[inline]
126  pub fn kind(&self) -> PrefectureBuildOrderKind {
127    self.kind
128  }
129
130  #[inline]
131  pub fn building(&self) -> BuildingId {
132    self.building
133  }
134
135  #[inline]
136  pub fn resources(&self) -> &Resources {
137    &self.resources
138  }
139}
140
141impl InfrastructureQueueOrder for PrefectureBuildOrder {
142  fn is_done(&self) -> bool {
143    self.state.is_done()
144  }
145
146  fn set_done(&mut self) {
147    self.state = PrefectureBuildOrderState::Done;
148  }
149
150  fn pending_workforce(&self) -> Option<Workforce> {
151    self.state.pending_workforce()
152  }
153
154  fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
155    self.state.pending_workforce_mut()
156  }
157}
158
159#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
160pub struct PrefectureBuildOrderId(Uuid);
161
162impl PrefectureBuildOrderId {
163  #[must_use]
164  pub fn new() -> Self {
165    Self(Uuid::new_v4())
166  }
167}
168
169impl Default for PrefectureBuildOrderId {
170  fn default() -> Self {
171    Self::new()
172  }
173}
174
175#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, EnumIs)]
176#[serde(rename_all = "kebab-case")]
177pub enum PrefectureBuildOrderKind {
178  Construction,
179  Demolition,
180}
181
182impl PrefectureBuildOrderKind {
183  fn apply_modifier(self, workforce: &mut Workforce) {
184    if let Self::Demolition = self {
185      *workforce = workforce.mul_ceil(0.5).into();
186    }
187  }
188}
189
190#[derive(Clone, Debug, EnumIs, Deserialize, Serialize)]
191#[serde(tag = "kind", rename_all = "kebab-case")]
192pub enum PrefectureBuildOrderState {
193  Pending { workforce: Workforce },
194  Done,
195}
196
197impl PrefectureBuildOrderState {
198  fn pending_workforce(&self) -> Option<Workforce> {
199    if let Self::Pending { workforce } = self { Some(*workforce) } else { None }
200  }
201
202  fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
203    if let Self::Pending { workforce } = self { Some(workforce) } else { None }
204  }
205}
206
207impl PrefectureBuildOrderState {
208  fn new(workforce: Workforce) -> Self {
209    Self::Pending { workforce }
210  }
211}
212
213#[derive(Clone, Debug, Deserialize, Serialize)]
214#[serde(rename_all = "camelCase")]
215pub struct PrefectureBuildOrderRequest {
216  pub coord: Coord,
217  pub building: BuildingId,
218  pub kind: PrefectureBuildOrderKind,
219}