Skip to main content

nil_core/world/
config.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use 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/// Proportion of bots that will have an advanced start with higher level infrastructure.
229#[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);