1use crate::error::{Error, Result};
5use crate::resources::Resources;
6use crate::resources::influence::Influence;
7use crate::world::World;
8use bon::Builder;
9use derive_more::{Display, 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(Debug, Display, From, Into, PartialEq, Eq, Hash, Deserialize, Serialize)]
128#[from(String, &str, Arc<str>, Box<str>, Cow<'_, str>)]
129#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
130pub struct PlayerId(Arc<str>);
131
132impl Clone for PlayerId {
133 fn clone(&self) -> Self {
134 Self(Arc::clone(&self.0))
135 }
136}
137
138impl AsRef<str> for PlayerId {
139 fn as_ref(&self) -> &str {
140 self.0.as_str()
141 }
142}
143
144impl Deref for PlayerId {
145 type Target = str;
146
147 fn deref(&self) -> &Self::Target {
148 self.0.as_str()
149 }
150}
151
152impl Borrow<str> for PlayerId {
153 fn borrow(&self) -> &str {
154 self.0.as_str()
155 }
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
159#[serde(rename_all = "kebab-case")]
160#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
161pub enum PlayerStatus {
162 Active,
163 Inactive,
164}
165
166#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
167#[serde(rename_all = "camelCase")]
168#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
169pub struct PlayerOptions {
170 #[builder(start_fn, into)]
171 pub id: PlayerId,
172}
173
174impl PlayerOptions {
175 #[inline]
176 pub fn into_player(self) -> Player {
177 Player::new(self)
178 }
179}
180
181#[derive(Clone, Debug, Deserialize, Serialize)]
182#[serde(rename_all = "camelCase")]
183#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
184pub struct PublicPlayer {
185 id: PlayerId,
186 status: PlayerStatus,
187}
188
189impl From<&Player> for PublicPlayer {
190 fn from(player: &Player) -> Self {
191 Self {
192 id: player.id.clone(),
193 status: player.status,
194 }
195 }
196}