1use crate::capital::{Capital, PublicCapital};
5use crate::error::{Error, Result};
6use crate::resources::Resources;
7use crate::resources::gold::Gold;
8use crate::resources::influence::Influence;
9use crate::ruler::Ruler;
10use crate::world::World;
11use bon::Builder;
12use derive_more::{From, Into};
13use serde::{Deserialize, Serialize};
14use std::borrow::{Borrow, Cow};
15use std::collections::HashMap;
16use std::ops::Deref;
17use std::sync::Arc;
18
19#[derive(Clone, Debug, Default, Deserialize, Serialize)]
20pub struct PlayerManager(HashMap<PlayerId, Player>);
21
22impl PlayerManager {
23 pub(crate) fn manage(&mut self, player: Player) -> Result<()> {
24 if self.0.contains_key(&player.id) {
25 return Err(Error::PlayerAlreadySpawned(player.id));
26 } else {
27 self.0.insert(player.id(), player);
28 }
29
30 Ok(())
31 }
32
33 pub fn player(&self, id: &PlayerId) -> Result<&Player> {
34 self
35 .0
36 .get(id)
37 .ok_or_else(|| Error::PlayerNotFound(id.clone()))
38 }
39
40 pub(crate) fn player_mut(&mut self, id: &PlayerId) -> Result<&mut Player> {
41 self
42 .0
43 .get_mut(id)
44 .ok_or_else(|| Error::PlayerNotFound(id.clone()))
45 }
46
47 pub fn players(&self) -> impl Iterator<Item = &Player> {
48 self.0.values()
49 }
50
51 pub fn player_ids(&self) -> impl Iterator<Item = &PlayerId> {
52 self.0.keys()
53 }
54
55 pub fn active_players(&self) -> impl Iterator<Item = &Player> {
56 self
57 .players()
58 .filter(|player| player.is_active())
59 }
60
61 #[inline]
62 pub fn has(&self, id: &PlayerId) -> bool {
63 self.0.contains_key(id)
64 }
65}
66
67#[derive(Clone, Debug, Deserialize, Serialize)]
68#[serde(rename_all = "camelCase")]
69#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
70pub struct Player {
71 id: PlayerId,
72 pub(crate) status: PlayerStatus,
73 pub(crate) capital: Capital,
74 pub(crate) resources: Resources,
75 pub(crate) gold: Gold,
76 pub(crate) influence: Influence,
77}
78
79impl Player {
80 pub fn new(options: PlayerOptions) -> Self {
81 Self {
82 id: options.id,
83 status: PlayerStatus::Active,
84 capital: Capital::default(),
85 resources: Resources::PLAYER,
86 gold: Gold::MIN,
87 influence: Influence::MIN,
88 }
89 }
90
91 pub fn spawn(self, world: &mut World) -> Result<()> {
92 world.spawn_player(self)
93 }
94
95 #[inline]
96 pub fn id(&self) -> PlayerId {
97 self.id.clone()
98 }
99
100 #[inline]
101 pub fn status(&self) -> PlayerStatus {
102 self.status
103 }
104
105 #[inline]
106 pub fn capital(&self) -> &Capital {
107 &self.capital
108 }
109
110 #[inline]
111 pub fn resources(&self) -> Resources {
112 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 capital: PublicCapital,
215}
216
217impl From<&Player> for PublicPlayer {
218 fn from(player: &Player) -> Self {
219 Self {
220 id: player.id.clone(),
221 status: player.status,
222 capital: PublicCapital::from(&player.capital),
223 }
224 }
225}