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      #[derive(Clone, Debug, Default, Deserialize, Serialize)]
95      #[serde(rename_all = "camelCase")]
96      pub struct [<$building RecruitQueue>] {
97        orders: VecDeque<[<$building RecruitOrder>]>,
98      }
99
100      impl [<$building RecruitQueue>] {
101        pub(crate) fn recruit(
102          &mut self,
103          request: &[<$building RecruitOrderRequest>],
104          current_resources: Option<&Resources>,
105        ) -> Result<&[<$building RecruitOrder>]> {
106          let unit = UnitBox::from(request.unit);
107          let chunk = unit.as_dyn().chunk();
108          let size = SquadSize::new(chunk.size() * request.chunks);
109          let resources = &chunk.resources() * request.chunks;
110          let workforce = chunk.workforce() * request.chunks;
111
112          if let Some(current_resources) = current_resources
113            && current_resources
114              .checked_sub(&resources)
115              .is_none()
116          {
117            return Err(Error::InsufficientResources);
118          }
119
120          self.orders.push_back([<$building RecruitOrder>] {
121            id: [<$building RecruitOrderId>]::new(),
122            squad: Squad::new(unit.id(), size),
123            resources,
124            workforce,
125            state: [<$building RecruitOrderState>]::new(workforce),
126          });
127
128          let len = self.orders.len();
129          Ok(unsafe {
130            self
131              .orders
132              .get(len.unchecked_sub(1))
133              .unwrap_unchecked()
134          })
135        }
136
137        /// Cancels a recruit order.
138        #[must_use]
139        pub(crate) fn cancel(&mut self, id: [<$building RecruitOrderId>]) -> Option<[<$building RecruitOrder>]> {
140          let position = self
141            .orders
142            .iter()
143            .position(|order| order.id == id)?;
144
145          self.orders.remove(position)
146        }
147      }
148
149      impl InfrastructureQueue<[<$building RecruitOrder>]> for [<$building RecruitQueue>] {
150        fn queue(&self) -> &VecDeque<[<$building RecruitOrder>]> {
151          &self.orders
152        }
153
154        fn queue_mut(&mut self) -> &mut VecDeque<[<$building RecruitOrder>]> {
155          &mut self.orders
156        }
157      }
158
159      #[must_use]
160      #[derive(Clone, Debug, Deserialize, Serialize)]
161      #[serde(rename_all = "camelCase")]
162      pub struct [<$building RecruitOrder>] {
163        id: [<$building RecruitOrderId>],
164        squad: Squad,
165        resources: Resources,
166        workforce: Workforce,
167        state: [<$building RecruitOrderState>],
168      }
169
170      impl [<$building RecruitOrder>] {
171        #[inline]
172        pub fn id(&self) -> [<$building RecruitOrderId>] {
173          self.id
174        }
175
176        #[inline]
177        pub fn squad(&self) -> &Squad {
178          &self.squad
179        }
180
181        #[inline]
182        pub fn resources(&self) -> &Resources {
183          &self.resources
184        }
185      }
186
187      impl From<[<$building RecruitOrder>]> for Squad {
188        fn from(order: [<$building RecruitOrder>]) -> Self {
189          order.squad
190        }
191      }
192
193      impl InfrastructureQueueOrder for [<$building RecruitOrder>] {
194        fn is_done(&self) -> bool {
195          self.state.is_done()
196        }
197
198        fn set_done(&mut self) {
199          self.state = [<$building RecruitOrderState>]::Done;
200        }
201
202        fn pending_workforce(&self) -> Option<Workforce> {
203          self.state.pending_workforce()
204        }
205
206        fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
207          self.state.pending_workforce_mut()
208        }
209      }
210
211      #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
212      pub struct [<$building RecruitOrderId>](Uuid);
213
214      impl [<$building RecruitOrderId>] {
215        #[must_use]
216        pub fn new() -> Self {
217          Self(Uuid::new_v4())
218        }
219      }
220
221      impl Default for [<$building RecruitOrderId>] {
222        fn default() -> Self {
223          Self::new()
224        }
225      }
226
227      #[derive(Clone, Debug, EnumIs, Deserialize, Serialize)]
228      #[serde(tag = "kind", rename_all = "kebab-case")]
229      pub enum [<$building RecruitOrderState>] {
230        Pending { workforce: Workforce },
231        Done,
232      }
233
234      impl [<$building RecruitOrderState>] {
235        fn pending_workforce(&self) -> Option<Workforce> {
236          if let Self::Pending { workforce } = self { Some(*workforce) } else { None }
237        }
238
239        fn pending_workforce_mut(&mut self) -> Option<&mut Workforce> {
240          if let Self::Pending { workforce } = self { Some(workforce) } else { None }
241        }
242      }
243
244      impl [<$building RecruitOrderState>] {
245        fn new(workforce: Workforce) -> Self {
246          Self::Pending { workforce }
247        }
248      }
249
250      #[derive(Clone, Debug, Deserialize, Serialize)]
251      #[serde(rename_all = "camelCase")]
252      pub struct [<$building RecruitOrderRequest>] {
253        pub coord: Coord,
254        pub unit: [<$building UnitId>],
255        pub chunks: NonZeroU32,
256      }
257    }
258  };
259}