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