1use crate::error::AnyResult;
5use crate::world::WorldOptions;
6use bon::Builder;
7use derive_more::Deref;
8use nil_util::{ConstDeref, F64Math};
9use serde::{Deserialize, Serialize};
10use strum::EnumString;
11use uuid::Uuid;
12
13#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
14#[serde(rename_all = "camelCase")]
15#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
16pub struct WorldConfig {
17 #[builder(start_fn, into)]
18 name: WorldName,
19
20 #[builder(skip)]
21 id: WorldId,
22
23 #[serde(default)]
24 #[builder(default)]
25 locale: Locale,
26
27 #[serde(default)]
28 #[builder(default)]
29 allow_cheats: bool,
30
31 #[serde(default)]
32 #[builder(default, into)]
33 speed: WorldSpeed,
34
35 #[serde(default)]
36 #[builder(default, into)]
37 unit_speed: WorldUnitSpeed,
38
39 #[serde(default)]
40 #[builder(default, into)]
41 bot_density: BotDensity,
42
43 #[serde(default)]
44 #[builder(default, into)]
45 bot_advanced_start_ratio: BotAdvancedStartRatio,
46}
47
48impl WorldConfig {
49 pub fn new(options: &WorldOptions) -> Self {
50 Self {
51 id: WorldId::new(),
52 name: options.name.clone(),
53 locale: options.locale,
54 allow_cheats: options.allow_cheats,
55 speed: options.speed,
56 unit_speed: options.unit_speed,
57 bot_density: options.bot_density,
58 bot_advanced_start_ratio: options.bot_advanced_start_ratio,
59 }
60 }
61
62 #[inline]
63 pub fn id(&self) -> WorldId {
64 self.id
65 }
66
67 #[inline]
68 pub fn name(&self) -> WorldName {
69 self.name.clone()
70 }
71
72 #[inline]
73 pub fn locale(&self) -> Locale {
74 self.locale
75 }
76
77 #[inline]
78 pub fn are_cheats_allowed(&self) -> bool {
79 self.allow_cheats
80 }
81
82 #[inline]
83 pub fn speed(&self) -> WorldSpeed {
84 self.speed
85 }
86
87 #[inline]
88 pub fn unit_speed(&self) -> WorldUnitSpeed {
89 self.unit_speed
90 }
91
92 #[inline]
93 pub fn bot_density(&self) -> BotDensity {
94 self.bot_density
95 }
96
97 #[inline]
98 pub fn bot_advanced_start_ratio(&self) -> BotAdvancedStartRatio {
99 self.bot_advanced_start_ratio
100 }
101}
102
103#[derive(
104 Clone,
105 Copy,
106 Debug,
107 Deref,
108 derive_more::Display,
109 PartialEq,
110 Eq,
111 PartialOrd,
112 Ord,
113 Hash,
114 Deserialize,
115 Serialize,
116)]
117#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
118pub struct WorldId(Uuid);
119
120impl WorldId {
121 #[must_use]
122 pub fn new() -> Self {
123 Self(Uuid::now_v7())
124 }
125}
126
127impl Default for WorldId {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl TryFrom<&str> for WorldId {
134 type Error = anyhow::Error;
135
136 fn try_from(value: &str) -> AnyResult<Self> {
137 Ok(Self(Uuid::try_parse(value)?))
138 }
139}
140
141#[derive(Clone, Debug, derive_more::Display, PartialEq, Eq, Deserialize, Serialize)]
142#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
143pub struct WorldName(Box<str>);
144
145impl<T: AsRef<str>> From<T> for WorldName {
146 fn from(value: T) -> Self {
147 Self(Box::from(value.as_ref()))
148 }
149}
150
151#[derive(Copy, Debug, strum::Display, EnumString, Deserialize, Serialize)]
152#[derive_const(Clone, Default)]
153#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
154pub enum Locale {
155 #[default]
156 #[serde(rename = "en-US")]
157 #[strum(serialize = "en-US")]
158 English,
159
160 #[serde(rename = "pt-BR")]
161 #[strum(serialize = "pt-BR")]
162 Portuguese,
163}
164
165macro_rules! impl_f64_newtype {
166 ($name:ident, min = $min:expr, max = $max:expr) => {
167 impl $name {
168 pub const MIN: Self = $name($min);
169 pub const MAX: Self = $name($max);
170
171 #[inline]
172 pub const fn new(value: f64) -> Self {
173 debug_assert!(value.is_finite());
174 debug_assert!(!value.is_subnormal());
175 Self(value.clamp(Self::MIN.0, Self::MAX.0))
176 }
177 }
178
179 impl const From<f64> for $name {
180 fn from(value: f64) -> Self {
181 Self::new(value)
182 }
183 }
184
185 impl const From<$name> for f64 {
186 fn from(value: $name) -> Self {
187 value.0
188 }
189 }
190 };
191 ($name:ident, min = $min:expr, max = $max:expr, default = $default:expr) => {
192 impl_f64_newtype!($name, min = $min, max = $max);
193
194 impl const Default for $name {
195 fn default() -> Self {
196 Self::new($default)
197 }
198 }
199 };
200}
201
202#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
203#[derive_const(Clone, PartialEq, PartialOrd)]
204#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
205pub struct WorldSpeed(f64);
206
207impl_f64_newtype!(WorldSpeed, min = 0.1, max = 10.0, default = 1.0);
208
209#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
210#[derive_const(Clone, PartialEq, PartialOrd)]
211#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
212pub struct WorldUnitSpeed(f64);
213
214impl_f64_newtype!(WorldUnitSpeed, min = 0.1, max = 10.0, default = 1.0);
215
216#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
217#[derive_const(Clone, PartialEq, PartialOrd)]
218#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
219pub struct BotDensity(f64);
220
221impl_f64_newtype!(
222 BotDensity,
223 min = 0.0,
224 max = 3.0,
225 default = if cfg!(target_os = "android") { 1.0 } else { 2.0 }
226);
227
228#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
230#[derive_const(Clone, PartialEq, PartialOrd)]
231#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
232pub struct BotAdvancedStartRatio(f64);
233
234impl_f64_newtype!(BotAdvancedStartRatio, min = 0.0, max = 1.0, default = 0.2);