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::{Display, EnumIs, VariantArray};
9
10#[derive(Builder, Copy, Debug, Deserialize, Serialize)]
11#[derive_const(Clone, 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, Display, EnumIs, Deserialize, Serialize, VariantArray)]
98#[derive_const(PartialEq, Eq)]
99#[serde(rename_all = "kebab-case")]
100#[strum(serialize_all = "kebab-case")]
101#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
102pub enum EthicPowerAxis {
103  Militarist,
104  FanaticMilitarist,
105  Pacifist,
106  FanaticPacifist,
107}
108
109impl EthicPowerAxis {
110  pub fn random() -> Self {
111    Self::VARIANTS
112      .choose_weighted(&mut rand::rng(), |ethic| {
113        match ethic {
114          Self::Militarist | Self::Pacifist => 4u8,
115          Self::FanaticMilitarist | Self::FanaticPacifist => 1u8,
116        }
117      })
118      .copied()
119      .expect("`Self::VARIANTS` should never be empty")
120  }
121
122  /// Whether this ethic is [`EthicPowerAxis::FanaticMilitarist`] or [`EthicPowerAxis::FanaticPacifist`].
123  pub const fn is_fanatic(&self) -> bool {
124    matches!(self, Self::FanaticMilitarist | Self::FanaticPacifist)
125  }
126
127  /// Whether this ethic is [`EthicPowerAxis::Militarist`] or [`EthicPowerAxis::FanaticMilitarist`].
128  pub const fn is_militarist_variant(&self) -> bool {
129    matches!(self, Self::Militarist | Self::FanaticMilitarist)
130  }
131
132  /// Whether this ethic is [`EthicPowerAxis::Pacifist`] or [`EthicPowerAxis::FanaticPacifist`].
133  pub const fn is_pacifist_variant(&self) -> bool {
134    matches!(self, Self::Pacifist | Self::FanaticPacifist)
135  }
136
137  pub const fn is_same_variant(&self, other: Self) -> bool {
138    (self.is_militarist_variant() && other.is_militarist_variant())
139      || (self.is_pacifist_variant() && other.is_pacifist_variant())
140  }
141}
142
143#[derive(Clone, Copy, Debug, Display, EnumIs, Deserialize, Serialize, VariantArray)]
144#[derive_const(PartialEq, Eq)]
145#[serde(rename_all = "kebab-case")]
146#[strum(serialize_all = "kebab-case")]
147#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
148pub enum EthicTruthAxis {
149  Materialist,
150  FanaticMaterialist,
151  Spiritualist,
152  FanaticSpiritualist,
153}
154
155impl EthicTruthAxis {
156  pub fn random() -> Self {
157    Self::VARIANTS
158      .choose_weighted(&mut rand::rng(), |ethic| {
159        match ethic {
160          Self::Materialist | Self::Spiritualist => 4u8,
161          Self::FanaticMaterialist | Self::FanaticSpiritualist => 1u8,
162        }
163      })
164      .copied()
165      .expect("`Self::VARIANTS` should never be empty")
166  }
167
168  /// Whether this ethic is [`EthicTruthAxis::FanaticMaterialist`] or [`EthicTruthAxis::FanaticSpiritualist`].
169  pub const fn is_fanatic(&self) -> bool {
170    matches!(self, Self::FanaticMaterialist | Self::FanaticSpiritualist)
171  }
172
173  /// Whether this ethic is [`EthicTruthAxis::Materialist`] or [`EthicTruthAxis::FanaticMaterialist`].
174  pub const fn is_materialist_variant(&self) -> bool {
175    matches!(self, Self::Materialist | Self::FanaticMaterialist)
176  }
177
178  /// Whether this ethic is [`EthicTruthAxis::Spiritualist`] or [`EthicTruthAxis::FanaticSpiritualist`].
179  pub const fn is_spiritualist_variant(&self) -> bool {
180    matches!(self, Self::Spiritualist | Self::FanaticSpiritualist)
181  }
182
183  pub const fn is_same_variant(&self, other: Self) -> bool {
184    (self.is_materialist_variant() && other.is_materialist_variant())
185      || (self.is_spiritualist_variant() && other.is_spiritualist_variant())
186  }
187}