use std::fmt::{Display, Formatter};
use std::sync::OnceLock;
use anyhow::{anyhow, Result};
use bitflags::bitflags;
use crate::helpers::display_choices;
static GAME: OnceLock<Game> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Game {
#[cfg(feature = "ck3")]
Ck3,
#[cfg(feature = "vic3")]
Vic3,
#[cfg(feature = "imperator")]
Imperator,
#[cfg(feature = "hoi4")]
Hoi4,
}
impl Game {
pub fn set(game: Game) -> Result<()> {
GAME.set(game).map_err(|_| anyhow!("tried to set game type twice"))?;
Ok(())
}
#[allow(clippy::self_named_constructors)] #[allow(unreachable_code)]
pub fn game() -> Game {
#[cfg(all(
feature = "ck3",
not(feature = "vic3"),
not(feature = "imperator"),
not(feature = "hoi4")
))]
return Game::Ck3;
#[cfg(all(
feature = "vic3",
not(feature = "ck3"),
not(feature = "imperator"),
not(feature = "hoi4")
))]
return Game::Vic3;
#[cfg(all(
feature = "imperator",
not(feature = "ck3"),
not(feature = "vic3"),
not(feature = "hoi4")
))]
return Game::Imperator;
#[cfg(all(
feature = "hoi4",
not(feature = "ck3"),
not(feature = "vic3"),
not(feature = "imperator")
))]
return Game::Hoi4;
*GAME.get().expect("internal error: don't know which game we are validating")
}
#[inline]
pub(crate) fn is_ck3() -> bool {
#[cfg(not(feature = "ck3"))]
return false;
#[cfg(all(
feature = "ck3",
not(feature = "vic3"),
not(feature = "imperator"),
not(feature = "hoi4")
))]
return true;
#[cfg(all(
feature = "ck3",
any(feature = "vic3", feature = "imperator", feature = "hoi4")
))]
return GAME.get() == Some(&Game::Ck3);
}
#[inline]
pub(crate) fn is_vic3() -> bool {
#[cfg(not(feature = "vic3"))]
return false;
#[cfg(all(
feature = "vic3",
not(feature = "ck3"),
not(feature = "imperator"),
not(feature = "hoi4")
))]
return true;
#[cfg(all(
feature = "vic3",
any(feature = "ck3", feature = "imperator", feature = "hoi4")
))]
return GAME.get() == Some(&Game::Vic3);
}
#[inline]
pub(crate) fn is_imperator() -> bool {
#[cfg(not(feature = "imperator"))]
return false;
#[cfg(all(
feature = "imperator",
not(feature = "ck3"),
not(feature = "vic3"),
not(feature = "hoi4")
))]
return true;
#[cfg(all(
feature = "imperator",
any(feature = "ck3", feature = "vic3", feature = "hoi4")
))]
return GAME.get() == Some(&Game::Imperator);
}
#[inline]
pub(crate) fn is_jomini() -> bool {
Game::is_ck3() || Game::is_vic3() || Game::is_imperator()
}
#[inline]
pub(crate) fn is_hoi4() -> bool {
#[cfg(not(feature = "hoi4"))]
return false;
#[cfg(all(
feature = "hoi4",
not(feature = "ck3"),
not(feature = "vic3"),
not(feature = "imperator")
))]
return true;
#[cfg(all(
feature = "hoi4",
any(feature = "ck3", feature = "vic3", feature = "imperator")
))]
return GAME.get() == Some(&Game::Hoi4);
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GameFlags: u8 {
const Ck3 = 0x01;
const Vic3 = 0x02;
const Imperator = 0x04;
const Hoi4 = 0x08;
}
}
impl GameFlags {
pub fn game() -> Self {
match Game::game() {
#[cfg(feature = "ck3")]
Game::Ck3 => GameFlags::Ck3,
#[cfg(feature = "vic3")]
Game::Vic3 => GameFlags::Vic3,
#[cfg(feature = "imperator")]
Game::Imperator => GameFlags::Imperator,
#[cfg(feature = "hoi4")]
Game::Hoi4 => GameFlags::Hoi4,
}
}
pub const fn jomini() -> Self {
GameFlags::Ck3.union(GameFlags::Vic3).union(GameFlags::Imperator)
}
}
impl Display for GameFlags {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let mut vec = Vec::new();
if self.contains(Self::Ck3) {
vec.push("Crusader Kings 3");
}
if self.contains(Self::Vic3) {
vec.push("Victoria 3");
}
if self.contains(Self::Imperator) {
vec.push("Imperator: Rome");
}
if self.contains(Self::Hoi4) {
vec.push("Hearts of Iron 4");
}
display_choices(f, &vec, "and")
}
}
impl From<Game> for GameFlags {
fn from(game: Game) -> Self {
match game {
#[cfg(feature = "ck3")]
Game::Ck3 => GameFlags::Ck3,
#[cfg(feature = "vic3")]
Game::Vic3 => GameFlags::Vic3,
#[cfg(feature = "imperator")]
Game::Imperator => GameFlags::Imperator,
#[cfg(feature = "hoi4")]
Game::Hoi4 => GameFlags::Hoi4,
}
}
}