Skip to main content

nil_core/
ethic.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use bon::Builder;
5use rand::seq::IndexedRandom;
6use serde::{Deserialize, Serialize};
7use std::fmt::Debug;
8use strum::{EnumIs, VariantArray};
9
10#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
11#[derive_const(PartialEq, Eq)]
12#[serde(rename_all = "camelCase")]
13#[builder(const)]
14#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
15pub struct Ethics {
16  power: EthicPowerAxis,
17  truth: EthicTruthAxis,
18}
19
20impl Ethics {
21  pub fn random() -> Self {
22    Self {
23      power: EthicPowerAxis::random(),
24      truth: EthicTruthAxis::random(),
25    }
26  }
27
28  #[inline]
29  pub const fn power(&self) -> EthicPowerAxis {
30    self.power
31  }
32
33  #[inline]
34  pub const fn truth(&self) -> EthicTruthAxis {
35    self.truth
36  }
37
38  #[inline]
39  pub const fn is_militarist(&self) -> bool {
40    self.power.is_militarist()
41  }
42
43  #[inline]
44  pub const fn is_fanatic_militarist(&self) -> bool {
45    self.power.is_fanatic_militarist()
46  }
47
48  #[inline]
49  pub const fn is_pacifist(&self) -> bool {
50    self.power.is_pacifist()
51  }
52
53  #[inline]
54  pub const fn is_fanatic_pacifist(&self) -> bool {
55    self.power.is_fanatic_pacifist()
56  }
57
58  #[inline]
59  pub const fn is_materialist(&self) -> bool {
60    self.truth.is_materialist()
61  }
62
63  #[inline]
64  pub const fn is_fanatic_materialist(&self) -> bool {
65    self.truth.is_fanatic_materialist()
66  }
67
68  #[inline]
69  pub const fn is_spiritualist(&self) -> bool {
70    self.truth.is_spiritualist()
71  }
72
73  #[inline]
74  pub const fn is_fanatic_spiritualist(&self) -> bool {
75    self.truth.is_fanatic_spiritualist()
76  }
77}
78
79impl From<EthicPowerAxis> for Ethics {
80  fn from(power: EthicPowerAxis) -> Self {
81    Self {
82      power,
83      truth: EthicTruthAxis::random(),
84    }
85  }
86}
87
88impl From<EthicTruthAxis> for Ethics {
89  fn from(truth: EthicTruthAxis) -> Self {
90    Self {
91      power: EthicPowerAxis::random(),
92      truth,
93    }
94  }
95}
96
97#[derive(Clone, Copy, Debug, EnumIs, Deserialize, Serialize, VariantArray)]
98#[derive_const(PartialEq, Eq)]
99#[serde(rename_all = "kebab-case")]
100#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
101pub enum EthicPowerAxis {
102  Militarist,
103  FanaticMilitarist,
104  Pacifist,
105  FanaticPacifist,
106}
107
108impl EthicPowerAxis {
109  pub fn random() -> Self {
110    Self::VARIANTS
111      .choose_weighted(&mut rand::rng(), |ethic| {
112        match ethic {
113          Self::Militarist | Self::Pacifist => 4u8,
114          Self::FanaticMilitarist | Self::FanaticPacifist => 1u8,
115        }
116      })
117      .copied()
118      .expect("`Self::VARIANTS` should never be empty")
119  }
120
121  /// Whether this ethic is [`EthicPowerAxis::FanaticMilitarist`] or [`EthicPowerAxis::FanaticPacifist`].
122  pub const fn is_fanatic(&self) -> bool {
123    matches!(self, Self::FanaticMilitarist | Self::FanaticPacifist)
124  }
125
126  /// Whether this ethic is [`EthicPowerAxis::Militarist`] or [`EthicPowerAxis::FanaticMilitarist`].
127  pub const fn is_militarist_variant(&self) -> bool {
128    matches!(self, Self::Militarist | Self::FanaticMilitarist)
129  }
130
131  /// Whether this ethic is [`EthicPowerAxis::Pacifist`] or [`EthicPowerAxis::FanaticPacifist`].
132  pub const fn is_pacifist_variant(&self) -> bool {
133    matches!(self, Self::Pacifist | Self::FanaticPacifist)
134  }
135
136  pub const fn is_same_variant(&self, other: Self) -> bool {
137    (self.is_militarist_variant() && other.is_militarist_variant())
138      || (self.is_pacifist_variant() && other.is_pacifist_variant())
139  }
140}
141
142#[derive(Clone, Copy, Debug, EnumIs, Deserialize, Serialize, VariantArray)]
143#[derive_const(PartialEq, Eq)]
144#[serde(rename_all = "kebab-case")]
145#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
146pub enum EthicTruthAxis {
147  Materialist,
148  FanaticMaterialist,
149  Spiritualist,
150  FanaticSpiritualist,
151}
152
153impl EthicTruthAxis {
154  pub fn random() -> Self {
155    Self::VARIANTS
156      .choose_weighted(&mut rand::rng(), |ethic| {
157        match ethic {
158          Self::Materialist | Self::Spiritualist => 4u8,
159          Self::FanaticMaterialist | Self::FanaticSpiritualist => 1u8,
160        }
161      })
162      .copied()
163      .expect("`Self::VARIANTS` should never be empty")
164  }
165
166  /// Whether this ethic is [`EthicTruthAxis::FanaticMaterialist`] or [`EthicTruthAxis::FanaticSpiritualist`].
167  pub const fn is_fanatic(&self) -> bool {
168    matches!(self, Self::FanaticMaterialist | Self::FanaticSpiritualist)
169  }
170
171  /// Whether this ethic is [`EthicTruthAxis::Materialist`] or [`EthicTruthAxis::FanaticMaterialist`].
172  pub const fn is_materialist_variant(&self) -> bool {
173    matches!(self, Self::Materialist | Self::FanaticMaterialist)
174  }
175
176  /// Whether this ethic is [`EthicTruthAxis::Spiritualist`] or [`EthicTruthAxis::FanaticSpiritualist`].
177  pub const fn is_spiritualist_variant(&self) -> bool {
178    matches!(self, Self::Spiritualist | Self::FanaticSpiritualist)
179  }
180
181  pub const fn is_same_variant(&self, other: Self) -> bool {
182    (self.is_materialist_variant() && other.is_materialist_variant())
183      || (self.is_spiritualist_variant() && other.is_spiritualist_variant())
184  }
185}