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