1use 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<RulerRef<'_>> for Ruler {
139 fn from(ruler: RulerRef<'_>) -> Self {
140 match ruler {
141 RulerRef::Bot(bot) => Self::Bot { id: bot.id() },
142 RulerRef::Player(player) => Self::Player { id: player.id() },
143 RulerRef::Precursor(precursor) => Self::Precursor { id: precursor.id() },
144 }
145 }
146}
147
148impl From<RulerRefMut<'_>> for Ruler {
149 fn from(ruler: RulerRefMut<'_>) -> Self {
150 match ruler {
151 RulerRefMut::Bot(bot) => Self::Bot { id: bot.id() },
152 RulerRefMut::Player(player) => Self::Player { id: player.id() },
153 RulerRefMut::Precursor(precursor) => Self::Precursor { id: precursor.id() },
154 }
155 }
156}
157
158impl PartialOrd for Ruler {
159 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
160 Some(self.cmp(other))
161 }
162}
163
164impl Ord for Ruler {
165 fn cmp(&self, other: &Self) -> cmp::Ordering {
166 #[allow(clippy::match_same_arms)]
167 match (self, other) {
168 (Self::Bot { id: a }, Self::Bot { id: b }) => a.cmp(b),
169 (Self::Player { id: a }, Self::Player { id: b }) => a.cmp(b),
170 (Self::Precursor { id: a }, Self::Precursor { id: b }) => a.cmp(b),
171 (Self::Bot { .. }, Self::Player { .. }) => cmp::Ordering::Less,
172 (Self::Bot { .. }, Self::Precursor { .. }) => cmp::Ordering::Greater,
173 (Self::Player { .. }, _) => cmp::Ordering::Greater,
174 (Self::Precursor { .. }, _) => cmp::Ordering::Less,
175 }
176 }
177}
178
179#[derive(EnumIs, TryUnwrap, Unwrap)]
180#[try_unwrap(ref)]
181#[unwrap(ref)]
182pub enum RulerRef<'a> {
183 Bot(&'a Bot),
184 Player(&'a Player),
185 Precursor(&'a dyn Precursor),
186}
187
188impl<'a> RulerRef<'a> {
189 pub fn ethics(&self) -> Option<&'a Ethics> {
190 match self {
191 Self::Bot(bot) => Some(bot.ethics()),
192 Self::Player(..) => None,
193 Self::Precursor(precursor) => Some(precursor.ethics()),
194 }
195 }
196
197 pub fn resources(&self) -> Resources {
198 match self {
199 Self::Bot(bot) => bot.resources(),
200 Self::Player(player) => player.resources(),
201 Self::Precursor(precursor) => precursor.resources(),
202 }
203 }
204
205 #[inline]
206 pub fn has_resources(&self, resources: Resources) -> bool {
207 self
208 .resources()
209 .checked_sub(resources)
210 .is_some()
211 }
212
213 pub fn gold(&self) -> Gold {
214 match self {
215 Self::Bot(bot) => bot.gold(),
216 Self::Player(player) => player.gold(),
217 Self::Precursor(precursor) => precursor.gold(),
218 }
219 }
220
221 pub fn influence(&self) -> Influence {
222 match self {
223 Self::Bot(bot) => bot.influence(),
224 Self::Player(player) => player.influence(),
225 Self::Precursor(precursor) => precursor.influence(),
226 }
227 }
228}
229
230impl<'a> From<&'a Bot> for RulerRef<'a> {
231 fn from(bot: &'a Bot) -> Self {
232 Self::Bot(bot)
233 }
234}
235
236impl<'a> From<&'a Player> for RulerRef<'a> {
237 fn from(player: &'a Player) -> Self {
238 Self::Player(player)
239 }
240}
241
242impl<'a> From<&'a dyn Precursor> for RulerRef<'a> {
243 fn from(precursor: &'a dyn Precursor) -> Self {
244 Self::Precursor(precursor)
245 }
246}
247
248#[derive(EnumIs, TryUnwrap, Unwrap)]
249#[try_unwrap(ref)]
250#[unwrap(ref)]
251pub enum RulerRefMut<'a> {
252 Bot(&'a mut Bot),
253 Player(&'a mut Player),
254 Precursor(&'a mut dyn Precursor),
255}
256
257impl<'a> RulerRefMut<'a> {
258 pub fn resources_mut(&'a mut self) -> &'a mut Resources {
259 match self {
260 Self::Bot(bot) => bot.resources_mut(),
261 Self::Player(player) => player.resources_mut(),
262 Self::Precursor(precursor) => precursor.resources_mut(),
263 }
264 }
265
266 pub fn withdraw_resources(&'a mut self, resources: Resources) -> Result<()> {
268 let ruler_resources = self.resources_mut();
269 match ruler_resources.checked_sub(resources) {
270 Some(result) => *ruler_resources = result,
271 None => return Err(Error::InsufficientResources),
272 }
273
274 Ok(())
275 }
276
277 pub fn take_resources(&mut self) -> Resources {
279 match self {
280 Self::Bot(bot) => mem::take(bot.resources_mut()),
281 Self::Player(player) => mem::take(player.resources_mut()),
282 Self::Precursor(precursor) => mem::take(precursor.resources_mut()),
283 }
284 }
285
286 pub fn gold_mut(&mut self) -> &mut Gold {
287 match self {
288 Self::Bot(bot) => bot.gold_mut(),
289 Self::Player(player) => player.gold_mut(),
290 Self::Precursor(precursor) => precursor.gold_mut(),
291 }
292 }
293
294 pub fn withdraw_gold(&mut self, gold: Gold) -> Result<()> {
296 let ruler_gold = self.gold_mut();
297 match ruler_gold.checked_sub(gold) {
298 Some(result) => *ruler_gold = result,
299 None => return Err(Error::InsufficientGold),
300 }
301
302 Ok(())
303 }
304}
305
306impl<'a> From<&'a mut Bot> for RulerRefMut<'a> {
307 fn from(bot: &'a mut Bot) -> Self {
308 Self::Bot(bot)
309 }
310}
311
312impl<'a> From<&'a mut Player> for RulerRefMut<'a> {
313 fn from(player: &'a mut Player) -> Self {
314 Self::Player(player)
315 }
316}
317
318impl<'a> From<&'a mut dyn Precursor> for RulerRefMut<'a> {
319 fn from(precursor: &'a mut dyn Precursor) -> Self {
320 Self::Precursor(precursor)
321 }
322}