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