1use std::fmt::{Display, Formatter};
4use std::sync::OnceLock;
5
6use anyhow::{anyhow, Result};
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 = "hoi4")]
26 Hoi4,
27}
28
29impl Game {
30 pub fn set(game: Game) -> Result<()> {
33 GAME.set(game).map_err(|_| anyhow!("tried to set game type twice"))?;
34 Ok(())
35 }
36
37 #[allow(clippy::self_named_constructors)] #[allow(unreachable_code)]
43 pub fn game() -> Game {
44 #[cfg(all(
45 feature = "ck3",
46 not(feature = "vic3"),
47 not(feature = "imperator"),
48 not(feature = "hoi4")
49 ))]
50 return Game::Ck3;
51 #[cfg(all(
52 feature = "vic3",
53 not(feature = "ck3"),
54 not(feature = "imperator"),
55 not(feature = "hoi4")
56 ))]
57 return Game::Vic3;
58 #[cfg(all(
59 feature = "imperator",
60 not(feature = "ck3"),
61 not(feature = "vic3"),
62 not(feature = "hoi4")
63 ))]
64 return Game::Imperator;
65 #[cfg(all(
66 feature = "hoi4",
67 not(feature = "ck3"),
68 not(feature = "vic3"),
69 not(feature = "imperator")
70 ))]
71 return Game::Hoi4;
72 *GAME.get().expect("internal error: don't know which game we are validating")
73 }
74
75 #[inline]
77 pub(crate) fn is_ck3() -> bool {
78 #[cfg(not(feature = "ck3"))]
79 return false;
80 #[cfg(all(
81 feature = "ck3",
82 not(feature = "vic3"),
83 not(feature = "imperator"),
84 not(feature = "hoi4")
85 ))]
86 return true;
87 #[cfg(all(
88 feature = "ck3",
89 any(feature = "vic3", feature = "imperator", feature = "hoi4")
90 ))]
91 return GAME.get() == Some(&Game::Ck3);
92 }
93
94 #[inline]
96 pub(crate) fn is_vic3() -> bool {
97 #[cfg(not(feature = "vic3"))]
98 return false;
99 #[cfg(all(
100 feature = "vic3",
101 not(feature = "ck3"),
102 not(feature = "imperator"),
103 not(feature = "hoi4")
104 ))]
105 return true;
106 #[cfg(all(
107 feature = "vic3",
108 any(feature = "ck3", feature = "imperator", feature = "hoi4")
109 ))]
110 return GAME.get() == Some(&Game::Vic3);
111 }
112
113 #[inline]
115 pub(crate) fn is_imperator() -> bool {
116 #[cfg(not(feature = "imperator"))]
117 return false;
118 #[cfg(all(
119 feature = "imperator",
120 not(feature = "ck3"),
121 not(feature = "vic3"),
122 not(feature = "hoi4")
123 ))]
124 return true;
125 #[cfg(all(
126 feature = "imperator",
127 any(feature = "ck3", feature = "vic3", feature = "hoi4")
128 ))]
129 return GAME.get() == Some(&Game::Imperator);
130 }
131
132 #[inline]
135 pub(crate) fn is_jomini() -> bool {
136 Game::is_ck3() || Game::is_vic3() || Game::is_imperator()
137 }
138
139 #[inline]
141 pub(crate) fn is_hoi4() -> bool {
142 #[cfg(not(feature = "hoi4"))]
143 return false;
144 #[cfg(all(
145 feature = "hoi4",
146 not(feature = "ck3"),
147 not(feature = "vic3"),
148 not(feature = "imperator")
149 ))]
150 return true;
151 #[cfg(all(
152 feature = "hoi4",
153 any(feature = "ck3", feature = "vic3", feature = "imperator")
154 ))]
155 return GAME.get() == Some(&Game::Hoi4);
156 }
157}
158
159bitflags! {
160 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
165 pub struct GameFlags: u8 {
166 const Ck3 = 0x01;
167 const Vic3 = 0x02;
168 const Imperator = 0x04;
169 const Hoi4 = 0x08;
170 }
171}
172
173impl GameFlags {
174 pub fn game() -> Self {
177 match Game::game() {
179 #[cfg(feature = "ck3")]
180 Game::Ck3 => GameFlags::Ck3,
181 #[cfg(feature = "vic3")]
182 Game::Vic3 => GameFlags::Vic3,
183 #[cfg(feature = "imperator")]
184 Game::Imperator => GameFlags::Imperator,
185 #[cfg(feature = "hoi4")]
186 Game::Hoi4 => GameFlags::Hoi4,
187 }
188 }
189
190 pub const fn jomini() -> Self {
191 GameFlags::Ck3.union(GameFlags::Vic3).union(GameFlags::Imperator)
192 }
193}
194
195impl Display for GameFlags {
196 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
197 let mut vec = Vec::new();
198 if self.contains(Self::Ck3) {
199 vec.push("Crusader Kings 3");
200 }
201 if self.contains(Self::Vic3) {
202 vec.push("Victoria 3");
203 }
204 if self.contains(Self::Imperator) {
205 vec.push("Imperator: Rome");
206 }
207 if self.contains(Self::Hoi4) {
208 vec.push("Hearts of Iron 4");
209 }
210 display_choices(f, &vec, "and")
211 }
212}
213
214impl From<Game> for GameFlags {
215 fn from(game: Game) -> Self {
217 match 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 = "hoi4")]
225 Game::Hoi4 => GameFlags::Hoi4,
226 }
227 }
228}