1use 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}