Skip to main content

nil_core/infrastructure/
queue.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::resources::workforce::Workforce;
5use std::collections::VecDeque;
6
7pub trait InfrastructureQueue<T>
8where
9  T: InfrastructureQueueOrder,
10{
11  fn queue(&self) -> &VecDeque<T>;
12  fn queue_mut(&mut self) -> &mut VecDeque<T>;
13
14  /// Consumes workforce until it runs out or the entire queue is completed.
15  fn process(&mut self, mut workforce: Workforce) -> Vec<T> {
16    let mut orders = Vec::new();
17    loop {
18      if workforce == 0 {
19        break;
20      }
21
22      match self
23        .queue_mut()
24        .pop_front_if(|order| order.consume(&mut workforce))
25      {
26        Some(order) => orders.push(order),
27        None => break,
28      }
29    }
30
31    if !orders.is_empty() {
32      self.queue_mut().shrink_to_fit();
33    }
34
35    orders
36  }
37
38  fn iter<'a>(&'a self) -> impl Iterator<Item = &'a T>
39  where
40    T: 'a,
41  {
42    self.queue().iter()
43  }
44
45  fn len(&self) -> usize {
46    self.queue().len()
47  }
48
49  fn is_empty(&self) -> bool {
50    self.queue().is_empty()
51  }
52
53  fn sum_pending_workforce(&self) -> Workforce {
54    self
55      .iter()
56      .filter_map(InfrastructureQueueOrder::pending_workforce)
57      .map(u32::from)
58      .sum::<u32>()
59      .into()
60  }
61}
62
63pub trait InfrastructureQueueOrder {
64  fn is_done(&self) -> bool;
65  fn set_done(&mut self);
66
67  fn pending_workforce(&self) -> Option<Workforce>;
68  fn pending_workforce_mut(&mut self) -> Option<&mut Workforce>;
69
70  fn consume(&mut self, workforce: &mut Workforce) -> bool {
71    if let Some(pending) = self.pending_workforce_mut() {
72      if *pending > 0 {
73        let previous = *pending;
74        *pending -= *workforce;
75
76        // Decreases the available workforce based on the quantity consumed by this order.
77        *workforce -= previous - *pending;
78      }
79
80      if *pending == 0 {
81        self.set_done();
82      }
83    }
84
85    self.is_done()
86  }
87}
88
89#[doc(hidden)]
90#[macro_export]
91macro_rules! decl_recruit_queue {
92  ($building:ident) => {
93    paste::paste! {
94      impl [<$building RecruitQueue>] {
95        pub(crate) fn recruit(
96          &mut self,
97          request: &[<$building RecruitOrderRequest>],
98          available_resources: Resources,
99        ) -> Result<&[<$building RecruitOrder>]> {
100          let unit = UnitBox::from(request.unit);
101          let chunk = unit.as_dyn().chunk();
102          let size = SquadSize::new(chunk.size() * request.chunks);
103          let resources = chunk.resources() * request.chunks;
104          let workforce = chunk.workforce() * request.chunks;
105
106          if available_resources.checked_sub(resources).is_none() {
107            return Err(Error::InsufficientResources);
108          }
109
110          self.orders.push_back([<$building RecruitOrder>] {
111            id: [<$building RecruitOrderId>]::new(),
112            squad: Squad::new(unit.id(), size),
113            resources,
114            workforce,
115            state: [<$building RecruitOrderState>]::new(workforce),
116          });
117
118          let len = self.orders.len();
119          Ok(unsafe {
120            self
121              .orders
122              .get(len.unchecked_sub(1))
123              .unwrap_unchecked()
124          })
125        }
126
127        /// Cancels a recruit order.
128        #[must_use]
129        pub(crate) fn cancel(&mut self, id: [<$building RecruitOrderId>]) -> Option<[<$building RecruitOrder>]> {
130          let position = self
131            .orders
132            .iter()
133            .position(|order| order.id == id)?;
134
135          self.orders.remove(position)
136        }
137      }
138
139      impl InfrastructureQueue<[<$building RecruitOrder>]> for [<$building RecruitQueue>] {
140        fn queue(&self) -> &VecDeque<[<$building RecruitOrder>]> {
141          &self.orders
142        }
143
144        fn queue_mut(&mut self) -> &mut VecDeque<[<$building RecruitOrder>]> {
145          &mut self.orders
146        }
147      }
148
149      #[must_use]
150      #[derive(Clone, Debug, Deserialize, Serialize)]
151      #[serde(rename_all = "camelCase")]
152      #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
153      pub struct [<$building RecruitOrder>] {
154        id: [<$building RecruitOrderId>],
155        squad: Squad,
156        resources: Resources,
157        workforce: Workforce,
158        state: [<$building RecruitOrderState>],
159      }
160
161      impl [<$building RecruitOrder>] {
162        #[inline]
163        pub fn id(&self) -> [<$building RecruitOrderId>] {
164          self.id
165        }
166
167        #[inline]
168        pub fn squad(&self) -> &Squad {
169          &self.squad
170        }
171
172        #[inline]
173        pub fn resources(&self) -> Resources {
174          self.resources
175        }
176      }
177
178      impl From<[<$building RecruitOrder>]> for Squad {
179        fn from(order: [<$building RecruitOrder>]) -> Self {
180          order.squad
181        }
182      }
183
184      impl InfrastructureQueueOrder for [<$building RecruitOrder>] {
185        fn is_done(&self) -> bool {
186          self.state.is_done()
187        }
188
189        fn set_done(&mut self) {
190          self.state = [<$building RecruitOrderState>]::Done;
191        }
192
193        fn pending_workforce(&self) -> Option<Workforce> {
194          self.state.pending_workforce()
195        }
196
197        fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
198          self.state.pending_workforce_mut()
199        }
200      }
201
202      #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
203      #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
204      pub struct [<$building RecruitOrderId>](Uuid);
205
206      impl [<$building RecruitOrderId>] {
207        #[must_use]
208        pub fn new() -> Self {
209          Self(Uuid::new_v4())
210        }
211      }
212
213      impl Default for [<$building RecruitOrderId>] {
214        fn default() -> Self {
215          Self::new()
216        }
217      }
218
219      #[derive(Clone, Debug, EnumIs, Deserialize, Serialize)]
220      #[serde(tag = "kind", rename_all = "kebab-case")]
221      #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
222      pub enum [<$building RecruitOrderState>] {
223        Pending { workforce: Workforce },
224        Done,
225      }
226
227      impl [<$building RecruitOrderState>] {
228        fn pending_workforce(&self) -> Option<Workforce> {
229          if let Self::Pending { workforce } = self { Some(*workforce) } else { None }
230        }
231
232        fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
233          if let Self::Pending { workforce } = self { Some(workforce) } else { None }
234        }
235      }
236
237      impl [<$building RecruitOrderState>] {
238        fn new(workforce: Workforce) -> Self {
239          Self::Pending { workforce }
240        }
241      }
242
243      #[derive(Builder, Clone, Debug, Deserialize, Serialize)]
244      #[serde(rename_all = "camelCase")]
245      #[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
246      pub struct [<$building RecruitOrderRequest>] {
247        #[builder(into)]
248        pub coord: Coord,
249        pub unit: [<$building UnitId>],
250        pub chunks: NonZeroU32,
251      }
252    }
253  };
254}