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