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