1use 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 pub const fn is_fanatic(&self) -> bool {
124 matches!(self, Self::FanaticMilitarist | Self::FanaticPacifist)
125 }
126
127 pub const fn is_militarist_variant(&self) -> bool {
129 matches!(self, Self::Militarist | Self::FanaticMilitarist)
130 }
131
132 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 pub const fn is_fanatic(&self) -> bool {
170 matches!(self, Self::FanaticMaterialist | Self::FanaticSpiritualist)
171 }
172
173 pub const fn is_materialist_variant(&self) -> bool {
175 matches!(self, Self::Materialist | Self::FanaticMaterialist)
176 }
177
178 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}