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, Display};
8use nil_util::{ConstDeref, 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, into)]
32  speed: WorldSpeed,
33
34  #[serde(default)]
35  #[builder(default, into)]
36  unit_speed: WorldUnitSpeed,
37
38  #[serde(default)]
39  #[builder(default, into)]
40  bot_density: BotDensity,
41
42  #[serde(default)]
43  #[builder(default, into)]
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(Copy, Debug, Deserialize, Serialize)]
140#[derive_const(Clone, 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    impl const From<f64> for $name {
166      fn from(value: f64) -> Self {
167        Self::new(value)
168      }
169    }
170
171    impl const From<$name> for f64 {
172      fn from(value: $name) -> Self {
173        value.0
174      }
175    }
176  };
177  ($name:ident, min = $min:expr, max = $max:expr, default = $default:expr) => {
178    impl_f64_newtype!($name, min = $min, max = $max);
179
180    impl const Default for $name {
181      fn default() -> Self {
182        Self::new($default)
183      }
184    }
185  };
186}
187
188#[derive(Copy, Debug, Deserialize, Serialize, ConstDeref, F64Math)]
189#[derive_const(Clone, PartialEq, PartialOrd)]
190#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
191pub struct WorldSpeed(f64);
192
193impl_f64_newtype!(WorldSpeed, min = 0.1, max = 10.0, default = 1.0);
194
195#[derive(Copy, Debug, Deserialize, Serialize, ConstDeref, F64Math)]
196#[derive_const(Clone, PartialEq, PartialOrd)]
197#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
198pub struct WorldUnitSpeed(f64);
199
200impl_f64_newtype!(WorldUnitSpeed, min = 0.1, max = 10.0, default = 1.0);
201
202#[derive(Copy, Debug, Deserialize, Serialize, ConstDeref, F64Math)]
203#[derive_const(Clone, PartialEq, PartialOrd)]
204#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
205pub struct BotDensity(f64);
206
207impl_f64_newtype!(
208  BotDensity,
209  min = 0.0,
210  max = 3.0,
211  default = if cfg!(target_os = "android") { 1.0 } else { 2.0 }
212);
213
214/// Proportion of bots that will have an advanced start with higher level infrastructure.
215#[derive(Copy, Debug, Deserialize, Serialize, ConstDeref, F64Math)]
216#[derive_const(Clone, PartialEq, PartialOrd)]
217#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
218pub struct BotAdvancedStartRatio(f64);
219
220impl_f64_newtype!(BotAdvancedStartRatio, min = 0.0, max = 1.0, default = 0.2);