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