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  #[inline]
121  pub fn influence(&self) -> Influence {
122    self.influence
123  }
124
125  #[inline]
126  pub fn is_active(&self) -> bool {
127    matches!(self.status, PlayerStatus::Active)
128  }
129
130  #[inline]
131  pub fn is_inactive(&self) -> bool {
132    matches!(self.status, PlayerStatus::Inactive)
133  }
134}
135
136#[derive(
137  Debug,
138  derive_more::Display,
139  From,
140  Into,
141  PartialEq,
142  Eq,
143  PartialOrd,
144  Ord,
145  Hash,
146  Deserialize,
147  Serialize,
148)]
149#[from(String, &str, Arc<str>, Box<str>, Cow<'_, str>)]
150#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
151pub struct PlayerId(Arc<str>);
152
153impl Clone for PlayerId {
154  fn clone(&self) -> Self {
155    Self(Arc::clone(&self.0))
156  }
157}
158
159impl AsRef<str> for PlayerId {
160  fn as_ref(&self) -> &str {
161    self.0.as_str()
162  }
163}
164
165impl Deref for PlayerId {
166  type Target = str;
167
168  fn deref(&self) -> &Self::Target {
169    self.0.as_str()
170  }
171}
172
173impl Borrow<str> for PlayerId {
174  fn borrow(&self) -> &str {
175    self.0.as_str()
176  }
177}
178
179impl PartialEq<Ruler> for PlayerId {
180  fn eq(&self, other: &Ruler) -> bool {
181    if let Ruler::Player { id } = other { self.eq(id) } else { false }
182  }
183}
184
185#[derive(Clone, Copy, Debug, strum::Display, PartialEq, Eq, Deserialize, Serialize)]
186#[serde(rename_all = "kebab-case")]
187#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
188pub enum PlayerStatus {
189  Active,
190  Inactive,
191}
192
193#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
194#[serde(rename_all = "camelCase")]
195#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
196pub struct PlayerOptions {
197  #[builder(start_fn, into)]
198  pub id: PlayerId,
199}
200
201impl PlayerOptions {
202  #[inline]
203  pub fn into_player(self) -> Player {
204    Player::new(self)
205  }
206}
207
208#[derive(Clone, Debug, Deserialize, Serialize)]
209#[serde(rename_all = "camelCase")]
210#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
211pub struct PublicPlayer {
212  id: PlayerId,
213  status: PlayerStatus,
214}
215
216impl From<&Player> for PublicPlayer {
217  fn from(player: &Player) -> Self {
218    Self {
219      id: player.id.clone(),
220      status: player.status,
221    }
222  }
223}