1use std::fmt::{Display, Formatter};
4use std::sync::OnceLock;
5
6use anyhow::{Result, anyhow};
7use bitflags::bitflags;
8
9use crate::helpers::display_choices;
10
11static GAME: OnceLock<Game> = OnceLock::new();
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum Game {
19 #[cfg(feature = "ck3")]
20 Ck3,
21 #[cfg(feature = "vic3")]
22 Vic3,
23 #[cfg(feature = "imperator")]
24 Imperator,
25 #[cfg(feature = "eu5")]
26 Eu5,
27 #[cfg(feature = "hoi4")]
28 Hoi4,
29}
30
31impl Game {
32 pub fn set(game: Game) -> Result<()> {
35 GAME.set(game).map_err(|_| anyhow!("tried to set game type twice"))?;
36 Ok(())
37 }
38
39 #[allow(clippy::self_named_constructors)] #[allow(unreachable_code)]
45 pub fn game() -> Game {
46 #[cfg(all(
47 feature = "ck3",
48 not(feature = "vic3"),
49 not(feature = "imperator"),
50 not(feature = "eu5"),
51 not(feature = "hoi4")
52 ))]
53 return Game::Ck3;
54 #[cfg(all(
55 feature = "vic3",
56 not(feature = "ck3"),
57 not(feature = "imperator"),
58 not(feature = "eu5"),
59 not(feature = "hoi4")
60 ))]
61 return Game::Vic3;
62 #[cfg(all(
63 feature = "imperator",
64 not(feature = "ck3"),
65 not(feature = "vic3"),
66 not(feature = "eu5"),
67 not(feature = "hoi4")
68 ))]
69 return Game::Imperator;
70 #[cfg(all(
71 feature = "hoi4",
72 not(feature = "ck3"),
73 not(feature = "vic3"),
74 not(feature = "imperator"),
75 not(feature = "eu5"),
76 ))]
77 return Game::Hoi4;
78 #[cfg(all(
79 feature = "eu5",
80 not(feature = "ck3"),
81 not(feature = "vic3"),
82 not(feature = "imperator"),
83 not(feature = "hoi4"),
84 ))]
85 return Game::Eu5;
86 *GAME.get().expect("internal error: don't know which game we are validating")
87 }
88
89 #[inline]
91 pub(crate) fn is_ck3() -> bool {
92 #[cfg(not(feature = "ck3"))]
93 return false;
94 #[cfg(all(
95 feature = "ck3",
96 not(feature = "vic3"),
97 not(feature = "imperator"),
98 not(feature = "eu5"),
99 not(feature = "hoi4")
100 ))]
101 return true;
102 #[cfg(all(
103 feature = "ck3",
104 any(feature = "vic3", feature = "imperator", feature = "eu5", feature = "hoi4")
105 ))]
106 return GAME.get() == Some(&Game::Ck3);
107 }
108
109 #[inline]
111 pub(crate) fn is_vic3() -> bool {
112 #[cfg(not(feature = "vic3"))]
113 return false;
114 #[cfg(all(
115 feature = "vic3",
116 not(feature = "ck3"),
117 not(feature = "imperator"),
118 not(feature = "eu5"),
119 not(feature = "hoi4")
120 ))]
121 return true;
122 #[cfg(all(
123 feature = "vic3",
124 any(feature = "ck3", feature = "imperator", feature = "eu5", feature = "hoi4")
125 ))]
126 return GAME.get() == Some(&Game::Vic3);
127 }
128
129 #[inline]
131 pub(crate) fn is_imperator() -> bool {
132 #[cfg(not(feature = "imperator"))]
133 return false;
134 #[cfg(all(
135 feature = "imperator",
136 not(feature = "ck3"),
137 not(feature = "vic3"),
138 not(feature = "eu5"),
139 not(feature = "hoi4")
140 ))]
141 return true;
142 #[cfg(all(
143 feature = "imperator",
144 any(feature = "ck3", feature = "vic3", feature = "hoi4", feature = "eu5")
145 ))]
146 return GAME.get() == Some(&Game::Imperator);
147 }
148
149 #[inline]
151 pub(crate) fn is_eu5() -> bool {
152 #[cfg(not(feature = "eu5"))]
153 return false;
154 #[cfg(all(
155 feature = "eu5",
156 not(feature = "ck3"),
157 not(feature = "vic3"),
158 not(feature = "imperator"),
159 not(feature = "hoi4")
160 ))]
161 return true;
162 #[cfg(all(
163 feature = "eu5",
164 any(feature = "ck3", feature = "vic3", feature = "hoi4", feature = "imperator")
165 ))]
166 return GAME.get() == Some(&Game::Eu5);
167 }
168
169 #[inline]
172 pub(crate) fn is_jomini() -> bool {
173 Game::is_ck3() || Game::is_vic3() || Game::is_imperator() || Game::is_eu5()
174 }
175
176 #[inline]
178 pub(crate) fn is_hoi4() -> bool {
179 #[cfg(not(feature = "hoi4"))]
180 return false;
181 #[cfg(all(
182 feature = "hoi4",
183 not(feature = "ck3"),
184 not(feature = "vic3"),
185 not(feature = "imperator"),
186 not(feature = "eu5")
187 ))]
188 return true;
189 #[cfg(all(
190 feature = "hoi4",
191 any(feature = "ck3", feature = "vic3", feature = "imperator", feature = "eu5")
192 ))]
193 return GAME.get() == Some(&Game::Hoi4);
194 }
195}
196
197bitflags! {
198 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
203 pub struct GameFlags: u8 {
204 const Ck3 = 0x01;
205 const Vic3 = 0x02;
206 const Imperator = 0x04;
207 const Eu5 = 0x08;
208 const Hoi4 = 0x10;
209 }
210}
211
212impl GameFlags {
213 pub fn game() -> Self {
216 match Game::game() {
218 #[cfg(feature = "ck3")]
219 Game::Ck3 => GameFlags::Ck3,
220 #[cfg(feature = "vic3")]
221 Game::Vic3 => GameFlags::Vic3,
222 #[cfg(feature = "imperator")]
223 Game::Imperator => GameFlags::Imperator,
224 #[cfg(feature = "eu5")]
225 Game::Eu5 => GameFlags::Eu5,
226 #[cfg(feature = "hoi4")]
227 Game::Hoi4 => GameFlags::Hoi4,
228 }
229 }
230
231 pub const fn jomini() -> Self {
232 GameFlags::Ck3.union(GameFlags::Vic3).union(GameFlags::Imperator).union(GameFlags::Eu5)
233 }
234}
235
236impl Display for GameFlags {
237 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
238 let mut vec = Vec::new();
239 if self.contains(Self::Ck3) {
240 vec.push("Crusader Kings 3");
241 }
242 if self.contains(Self::Vic3) {
243 vec.push("Victoria 3");
244 }
245 if self.contains(Self::Imperator) {
246 vec.push("Imperator: Rome");
247 }
248 if self.contains(Self::Eu5) {
249 vec.push("Europa Universalis 5");
250 }
251 if self.contains(Self::Hoi4) {
252 vec.push("Hearts of Iron 4");
253 }
254 display_choices(f, &vec, "and")
255 }
256}
257
258impl From<Game> for GameFlags {
259 fn from(game: Game) -> Self {
261 match game {
262 #[cfg(feature = "ck3")]
263 Game::Ck3 => GameFlags::Ck3,
264 #[cfg(feature = "vic3")]
265 Game::Vic3 => GameFlags::Vic3,
266 #[cfg(feature = "imperator")]
267 Game::Imperator => GameFlags::Imperator,
268 #[cfg(feature = "eu5")]
269 Game::Eu5 => GameFlags::Eu5,
270 #[cfg(feature = "hoi4")]
271 Game::Hoi4 => GameFlags::Hoi4,
272 }
273 }
274}