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