Skip to main content

nil_core/
player.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::error::{Error, Result};
5use crate::resources::Resources;
6use crate::resources::gold::Gold;
7use crate::resources::influence::Influence;
8use crate::ruler::Ruler;
9use crate::world::World;
10use bon::Builder;
11use derive_more::{From, Into};
12use serde::{Deserialize, Serialize};
13use std::borrow::{Borrow, Cow};
14use std::collections::HashMap;
15use std::ops::Deref;
16use std::sync::Arc;
17
18#[derive(Clone, Debug, Default, Deserialize, Serialize)]
19pub struct PlayerManager(HashMap<PlayerId, Player>);
20
21impl PlayerManager {
22  pub(crate) fn manage(&mut self, player: Player) -> Result<()> {
23    if self.0.contains_key(&player.id) {
24      return Err(Error::PlayerAlreadySpawned(player.id));
25    } else {
26      self.0.insert(player.id(), player);
27    }
28
29    Ok(())
30  }
31
32  pub fn player(&self, id: &PlayerId) -> Result<&Player> {
33    self
34      .0
35      .get(id)
36      .ok_or_else(|| Error::PlayerNotFound(id.clone()))
37  }
38
39  pub(crate) fn player_mut(&mut self, id: &PlayerId) -> Result<&mut Player> {
40    self
41      .0
42      .get_mut(id)
43      .ok_or_else(|| Error::PlayerNotFound(id.clone()))
44  }
45
46  pub fn players(&self) -> impl Iterator<Item = &Player> {
47    self.0.values()
48  }
49
50  pub fn player_ids(&self) -> impl Iterator<Item = &PlayerId> {
51    self.0.keys()
52  }
53
54  pub fn active_players(&self) -> impl Iterator<Item = &Player> {
55    self
56      .players()
57      .filter(|player| player.is_active())
58  }
59
60  #[inline]
61  pub fn has(&self, id: &PlayerId) -> bool {
62    self.0.contains_key(id)
63  }
64}
65
66#[derive(Clone, Debug, Deserialize, Serialize)]
67#[serde(rename_all = "camelCase")]
68#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
69pub struct Player {
70  id: PlayerId,
71  status: PlayerStatus,
72  resources: Resources,
73  gold: Gold,
74  influence: Influence,
75}
76
77impl Player {
78  pub fn new(options: PlayerOptions) -> Self {
79    Self {
80      id: options.id,
81      status: PlayerStatus::Active,
82      resources: Resources::PLAYER,
83      gold: Gold::MIN,
84      influence: Influence::MIN,
85    }
86  }
87
88  pub fn spawn(self, world: &mut World) -> Result<()> {
89    world.spawn_player(self)
90  }
91
92  #[inline]
93  pub fn id(&self) -> PlayerId {
94    self.id.clone()
95  }
96
97  #[inline]
98  pub fn status(&self) -> PlayerStatus {
99    self.status
100  }
101
102  pub(crate) fn status_mut(&mut self) -> &mut PlayerStatus {
103    &mut self.status
104  }
105
106  #[inline]
107  pub fn resources(&self) -> Resources {
108    self.resources
109  }
110
111  pub(crate) fn resources_mut(&mut self) -> &mut Resources {
112    &mut self.resources
113  }
114
115  #[inline]
116  pub fn gold(&self) -> Gold {
117    self.gold
118  }
119
120  pub(crate) fn gold_mut(&mut self) -> &mut Gold {
121    &mut self.gold
122  }
123
124  #[inline]
125  pub fn influence(&self) -> Influence {
126    self.influence
127  }
128
129  #[inline]
130  pub fn is_active(&self) -> bool {
131    matches!(self.status, PlayerStatus::Active)
132  }
133
134  #[inline]
135  pub fn is_inactive(&self) -> bool {
136    matches!(self.status, PlayerStatus::Inactive)
137  }
138}
139
140#[derive(
141  Debug,
142  derive_more::Display,
143  From,
144  Into,
145  PartialEq,
146  Eq,
147  PartialOrd,
148  Ord,
149  Hash,
150  Deserialize,
151  Serialize,
152)]
153#[from(String, &str, Arc<str>, Box<str>, Cow<'_, str>)]
154#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
155pub struct PlayerId(Arc<str>);
156
157impl Clone for PlayerId {
158  fn clone(&self) -> Self {
159    Self(Arc::clone(&self.0))
160  }
161}
162
163impl AsRef<str> for PlayerId {
164  fn as_ref(&self) -> &str {
165    self.0.as_str()
166  }
167}
168
169impl Deref for PlayerId {
170  type Target = str;
171
172  fn deref(&self) -> &Self::Target {
173    self.0.as_str()
174  }
175}
176
177impl Borrow<str> for PlayerId {
178  fn borrow(&self) -> &str {
179    self.0.as_str()
180  }
181}
182
183impl PartialEq<Ruler> for PlayerId {
184  fn eq(&self, other: &Ruler) -> bool {
185    if let Ruler::Player { id } = other { self.eq(id) } else { false }
186  }
187}
188
189#[derive(Clone, Copy, Debug, strum::Display, PartialEq, Eq, Deserialize, Serialize)]
190#[serde(rename_all = "kebab-case")]
191#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
192pub enum PlayerStatus {
193  Active,
194  Inactive,
195}
196
197#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
198#[serde(rename_all = "camelCase")]
199#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
200pub struct PlayerOptions {
201  #[builder(start_fn, into)]
202  pub id: PlayerId,
203}
204
205impl PlayerOptions {
206  #[inline]
207  pub fn into_player(self) -> Player {
208    Player::new(self)
209  }
210}
211
212#[derive(Clone, Debug, Deserialize, Serialize)]
213#[serde(rename_all = "camelCase")]
214#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
215pub struct PublicPlayer {
216  id: PlayerId,
217  status: PlayerStatus,
218}
219
220impl From<&Player> for PublicPlayer {
221  fn from(player: &Player) -> Self {
222    Self {
223      id: player.id.clone(),
224      status: player.status,
225    }
226  }
227}