Skip to main content

nil_core/
ruler.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::error::{Error, Result};
5use crate::ethic::Ethics;
6use crate::npc::bot::{Bot, BotId};
7use crate::npc::precursor::{Precursor, PrecursorId};
8use crate::player::{Player, PlayerId};
9use crate::resources::Resources;
10use crate::resources::gold::Gold;
11use crate::resources::influence::Influence;
12use derive_more::{TryUnwrap, Unwrap};
13use serde::{Deserialize, Serialize};
14use std::{cmp, fmt, mem};
15use strum::EnumIs;
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize, EnumIs)]
18#[serde(tag = "kind", rename_all = "kebab-case")]
19#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
20pub enum Ruler {
21  Bot { id: BotId },
22  Player { id: PlayerId },
23  Precursor { id: PrecursorId },
24}
25
26impl Ruler {
27  #[inline]
28  pub fn bot(&self) -> Option<&BotId> {
29    if let Self::Bot { id } = self { Some(id) } else { None }
30  }
31
32  #[inline]
33  pub fn player(&self) -> Option<&PlayerId> {
34    if let Self::Player { id } = self { Some(id) } else { None }
35  }
36
37  #[inline]
38  pub fn precursor(&self) -> Option<PrecursorId> {
39    if let Self::Precursor { id } = self { Some(*id) } else { None }
40  }
41
42  pub fn is_bot_and<F>(&self, f: F) -> bool
43  where
44    F: FnOnce(&BotId) -> bool,
45  {
46    self.bot().is_some_and(f)
47  }
48
49  pub fn is_player_and<F>(&self, f: F) -> bool
50  where
51    F: FnOnce(&PlayerId) -> bool,
52  {
53    self.player().is_some_and(f)
54  }
55
56  pub fn is_precursor_and<F>(&self, f: F) -> bool
57  where
58    F: FnOnce(PrecursorId) -> bool,
59  {
60    self.precursor().is_some_and(f)
61  }
62}
63
64impl AsRef<str> for Ruler {
65  fn as_ref(&self) -> &str {
66    match self {
67      Self::Bot { id } => id.as_str(),
68      Self::Player { id } => id.as_str(),
69      Self::Precursor { id } => id.as_ref(),
70    }
71  }
72}
73
74impl fmt::Display for Ruler {
75  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76    match self {
77      Self::Bot { id } => id.fmt(f),
78      Self::Player { id } => id.fmt(f),
79      Self::Precursor { id } => id.fmt(f),
80    }
81  }
82}
83
84impl From<&Bot> for Ruler {
85  fn from(bot: &Bot) -> Self {
86    Self::Bot { id: bot.id() }
87  }
88}
89
90impl From<BotId> for Ruler {
91  fn from(id: BotId) -> Self {
92    Self::Bot { id }
93  }
94}
95
96impl From<&BotId> for Ruler {
97  fn from(id: &BotId) -> Self {
98    Self::Bot { id: id.clone() }
99  }
100}
101
102impl From<&Player> for Ruler {
103  fn from(player: &Player) -> Self {
104    Self::Player { id: player.id() }
105  }
106}
107
108impl From<PlayerId> for Ruler {
109  fn from(id: PlayerId) -> Self {
110    Self::Player { id }
111  }
112}
113
114impl From<&PlayerId> for Ruler {
115  fn from(id: &PlayerId) -> Self {
116    Self::Player { id: id.clone() }
117  }
118}
119
120impl From<&dyn Precursor> for Ruler {
121  fn from(precursor: &dyn Precursor) -> Self {
122    Self::Precursor { id: precursor.id() }
123  }
124}
125
126impl<T: Precursor> From<&T> for Ruler {
127  fn from(precursor: &T) -> Self {
128    Self::Precursor { id: precursor.id() }
129  }
130}
131
132impl From<PrecursorId> for Ruler {
133  fn from(id: PrecursorId) -> Self {
134    Self::Precursor { id }
135  }
136}
137
138impl From<&Ruler> for Ruler {
139  fn from(ruler: &Ruler) -> Self {
140    ruler.clone()
141  }
142}
143
144impl From<RulerRef<'_>> for Ruler {
145  fn from(ruler: RulerRef<'_>) -> Self {
146    match ruler {
147      RulerRef::Bot(bot) => Self::Bot { id: bot.id() },
148      RulerRef::Player(player) => Self::Player { id: player.id() },
149      RulerRef::Precursor(precursor) => Self::Precursor { id: precursor.id() },
150    }
151  }
152}
153
154impl From<RulerRefMut<'_>> for Ruler {
155  fn from(ruler: RulerRefMut<'_>) -> Self {
156    match ruler {
157      RulerRefMut::Bot(bot) => Self::Bot { id: bot.id() },
158      RulerRefMut::Player(player) => Self::Player { id: player.id() },
159      RulerRefMut::Precursor(precursor) => Self::Precursor { id: precursor.id() },
160    }
161  }
162}
163
164impl PartialOrd for Ruler {
165  fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
166    Some(self.cmp(other))
167  }
168}
169
170impl Ord for Ruler {
171  fn cmp(&self, other: &Self) -> cmp::Ordering {
172    #[allow(clippy::match_same_arms)]
173    match (self, other) {
174      (Self::Bot { id: a }, Self::Bot { id: b }) => a.cmp(b),
175      (Self::Player { id: a }, Self::Player { id: b }) => a.cmp(b),
176      (Self::Precursor { id: a }, Self::Precursor { id: b }) => a.cmp(b),
177      (Self::Bot { .. }, Self::Player { .. }) => cmp::Ordering::Less,
178      (Self::Bot { .. }, Self::Precursor { .. }) => cmp::Ordering::Greater,
179      (Self::Player { .. }, _) => cmp::Ordering::Greater,
180      (Self::Precursor { .. }, _) => cmp::Ordering::Less,
181    }
182  }
183}
184
185#[derive(EnumIs, TryUnwrap, Unwrap)]
186#[try_unwrap(ref)]
187#[unwrap(ref)]
188pub enum RulerRef<'a> {
189  Bot(&'a Bot),
190  Player(&'a Player),
191  Precursor(&'a dyn Precursor),
192}
193
194impl<'a> RulerRef<'a> {
195  pub fn ethics(&self) -> Option<&'a Ethics> {
196    match self {
197      Self::Bot(bot) => Some(bot.ethics()),
198      Self::Player(..) => None,
199      Self::Precursor(precursor) => Some(precursor.ethics()),
200    }
201  }
202
203  pub fn resources(&self) -> Resources {
204    match self {
205      Self::Bot(bot) => bot.resources(),
206      Self::Player(player) => player.resources(),
207      Self::Precursor(precursor) => precursor.resources(),
208    }
209  }
210
211  #[inline]
212  pub fn has_resources(&self, resources: Resources) -> bool {
213    self
214      .resources()
215      .checked_sub(resources)
216      .is_some()
217  }
218
219  pub fn gold(&self) -> Gold {
220    match self {
221      Self::Bot(bot) => bot.gold(),
222      Self::Player(player) => player.gold(),
223      Self::Precursor(precursor) => precursor.gold(),
224    }
225  }
226
227  pub fn influence(&self) -> Influence {
228    match self {
229      Self::Bot(bot) => bot.influence(),
230      Self::Player(player) => player.influence(),
231      Self::Precursor(precursor) => precursor.influence(),
232    }
233  }
234}
235
236impl<'a> From<&'a Bot> for RulerRef<'a> {
237  fn from(bot: &'a Bot) -> Self {
238    Self::Bot(bot)
239  }
240}
241
242impl<'a> From<&'a Player> for RulerRef<'a> {
243  fn from(player: &'a Player) -> Self {
244    Self::Player(player)
245  }
246}
247
248impl<'a> From<&'a dyn Precursor> for RulerRef<'a> {
249  fn from(precursor: &'a dyn Precursor) -> Self {
250    Self::Precursor(precursor)
251  }
252}
253
254#[derive(EnumIs, TryUnwrap, Unwrap)]
255#[try_unwrap(ref)]
256#[unwrap(ref)]
257pub enum RulerRefMut<'a> {
258  Bot(&'a mut Bot),
259  Player(&'a mut Player),
260  Precursor(&'a mut dyn Precursor),
261}
262
263impl<'a> RulerRefMut<'a> {
264  pub fn resources_mut(&'a mut self) -> &'a mut Resources {
265    match self {
266      Self::Bot(bot) => bot.resources_mut(),
267      Self::Player(player) => player.resources_mut(),
268      Self::Precursor(precursor) => precursor.resources_mut(),
269    }
270  }
271
272  /// Withdraws the specified resources from the ruler.
273  pub fn withdraw_resources(&'a mut self, resources: Resources) -> Result<()> {
274    let ruler_resources = self.resources_mut();
275    match ruler_resources.checked_sub(resources) {
276      Some(result) => *ruler_resources = result,
277      None => return Err(Error::InsufficientResources),
278    }
279
280    Ok(())
281  }
282
283  /// Takes all resources from the ruler, leaving them with nothing.
284  pub fn take_resources(&mut self) -> Resources {
285    match self {
286      Self::Bot(bot) => mem::take(bot.resources_mut()),
287      Self::Player(player) => mem::take(player.resources_mut()),
288      Self::Precursor(precursor) => mem::take(precursor.resources_mut()),
289    }
290  }
291
292  pub fn gold_mut(&mut self) -> &mut Gold {
293    match self {
294      Self::Bot(bot) => bot.gold_mut(),
295      Self::Player(player) => player.gold_mut(),
296      Self::Precursor(precursor) => precursor.gold_mut(),
297    }
298  }
299
300  /// Withdraws the specified amount of gold from the ruler.
301  pub fn withdraw_gold(&mut self, gold: Gold) -> Result<()> {
302    let ruler_gold = self.gold_mut();
303    match ruler_gold.checked_sub(gold) {
304      Some(result) => *ruler_gold = result,
305      None => return Err(Error::InsufficientGold),
306    }
307
308    Ok(())
309  }
310}
311
312impl<'a> From<&'a mut Bot> for RulerRefMut<'a> {
313  fn from(bot: &'a mut Bot) -> Self {
314    Self::Bot(bot)
315  }
316}
317
318impl<'a> From<&'a mut Player> for RulerRefMut<'a> {
319  fn from(player: &'a mut Player) -> Self {
320    Self::Player(player)
321  }
322}
323
324impl<'a> From<&'a mut dyn Precursor> for RulerRefMut<'a> {
325  fn from(precursor: &'a mut dyn Precursor) -> Self {
326    Self::Precursor(precursor)
327  }
328}