use crate::error::{Error, Result};
use crate::resources::Resources;
use crate::resources::influence::Influence;
use crate::world::World;
use bon::Builder;
use derive_more::{Display, From, Into};
use serde::{Deserialize, Serialize};
use std::borrow::{Borrow, Cow};
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct PlayerManager(HashMap<PlayerId, Player>);
impl PlayerManager {
pub(crate) fn manage(&mut self, player: Player) -> Result<()> {
if self.0.contains_key(&player.id) {
return Err(Error::PlayerAlreadySpawned(player.id));
} else {
self.0.insert(player.id(), player);
}
Ok(())
}
pub fn player(&self, id: &PlayerId) -> Result<&Player> {
self
.0
.get(id)
.ok_or_else(|| Error::PlayerNotFound(id.clone()))
}
pub(crate) fn player_mut(&mut self, id: &PlayerId) -> Result<&mut Player> {
self
.0
.get_mut(id)
.ok_or_else(|| Error::PlayerNotFound(id.clone()))
}
pub fn players(&self) -> impl Iterator<Item = &Player> {
self.0.values()
}
pub fn player_ids(&self) -> impl Iterator<Item = &PlayerId> {
self.0.keys()
}
pub fn active_players(&self) -> impl Iterator<Item = &Player> {
self
.players()
.filter(|player| player.is_active())
}
#[inline]
pub fn has(&self, id: &PlayerId) -> bool {
self.0.contains_key(id)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct Player {
id: PlayerId,
status: PlayerStatus,
resources: Resources,
influence: Influence,
}
impl Player {
pub fn new(options: PlayerOptions) -> Self {
Self {
id: options.id,
status: PlayerStatus::Active,
resources: Resources::PLAYER.clone(),
influence: Influence::MIN,
}
}
pub fn spawn(self, world: &mut World) -> Result<()> {
world.spawn_player(self)
}
#[inline]
pub fn id(&self) -> PlayerId {
self.id.clone()
}
#[inline]
pub fn status(&self) -> PlayerStatus {
self.status
}
pub(crate) fn status_mut(&mut self) -> &mut PlayerStatus {
&mut self.status
}
#[inline]
pub fn resources(&self) -> &Resources {
&self.resources
}
pub(crate) fn resources_mut(&mut self) -> &mut Resources {
&mut self.resources
}
#[inline]
pub fn influence(&self) -> Influence {
self.influence
}
#[inline]
pub fn is_active(&self) -> bool {
matches!(self.status, PlayerStatus::Active)
}
#[inline]
pub fn is_inactive(&self) -> bool {
matches!(self.status, PlayerStatus::Inactive)
}
}
#[derive(Debug, Display, From, Into, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[from(String, &str, Arc<str>, Box<str>, Cow<'_, str>)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct PlayerId(Arc<str>);
impl Clone for PlayerId {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl AsRef<str> for PlayerId {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}
impl Deref for PlayerId {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0.as_str()
}
}
impl Borrow<str> for PlayerId {
fn borrow(&self) -> &str {
self.0.as_str()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub enum PlayerStatus {
Active,
Inactive,
}
#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct PlayerOptions {
#[builder(start_fn, into)]
pub id: PlayerId,
}
impl PlayerOptions {
#[inline]
pub fn into_player(self) -> Player {
Player::new(self)
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct PublicPlayer {
id: PlayerId,
status: PlayerStatus,
}
impl From<&Player> for PublicPlayer {
fn from(player: &Player) -> Self {
Self {
id: player.id.clone(),
status: player.status,
}
}
}