use core::{fmt::Display, num::NonZeroU8}; use serde::{Deserialize, Serialize}; use crate::{ diedto::DiedTo, error::GameError, game::{DateTime, Village}, message::{Identification, PublicIdentity, Target, night::ActionPrompt}, modifier::Modifier, role::{MAPLE_WOLF_ABSTAIN_LIMIT, PreviousGuardianAction, Role, RoleTitle}, }; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct PlayerId(uuid::Uuid); impl PlayerId { pub fn new() -> Self { Self(uuid::Uuid::new_v4()) } pub const fn from_u128(v: u128) -> Self { Self(uuid::Uuid::from_u128(v)) } } impl Display for PlayerId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct CharacterId(uuid::Uuid); impl CharacterId { pub fn new() -> Self { Self(uuid::Uuid::new_v4()) } pub const fn from_u128(v: u128) -> Self { Self(uuid::Uuid::from_u128(v)) } } impl Display for CharacterId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Player { id: PlayerId, name: String, } impl Player { pub fn new(name: String) -> Self { Self { id: PlayerId::new(), name, } } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Protection { Guardian { source: CharacterId, guarding: bool }, Protector { source: CharacterId }, } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum KillOutcome { Killed, Failed, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Character { player_id: PlayerId, character_id: CharacterId, public: PublicIdentity, role: Role, modifier: Option, died_to: Option, role_changes: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct RoleChange { role: Role, new_role: RoleTitle, changed_on_night: u8, } impl Character { pub fn new(Identification { player_id, public }: Identification, role: Role) -> Self { Self { role, public, player_id, character_id: CharacterId::new(), modifier: None, died_to: None, role_changes: Vec::new(), } } pub fn target(&self) -> Target { Target { character_id: self.character_id.clone(), public: self.public.clone(), } } pub const fn public_identity(&self) -> &PublicIdentity { &self.public } pub fn name(&self) -> &str { &self.public.name } pub const fn number(&self) -> NonZeroU8 { self.public.number } pub const fn pronouns(&self) -> Option<&str> { match self.public.pronouns.as_ref() { Some(p) => Some(p.as_str()), None => None, } } pub fn died_to(&self) -> Option<&DiedTo> { self.died_to.as_ref() } pub fn kill(&mut self, died_to: DiedTo) { match &self.died_to { Some(_) => {} None => self.died_to = Some(died_to), } } pub const fn alive(&self) -> bool { self.died_to.is_none() } pub fn execute(&mut self, day: NonZeroU8) -> Result<(), GameError> { if self.died_to.is_some() { return Err(GameError::CharacterAlreadyDead); } self.died_to = Some(DiedTo::Execution { day }); Ok(()) } pub const fn character_id(&self) -> &CharacterId { &self.character_id } pub const fn player_id(&self) -> &PlayerId { &self.player_id } pub const fn role(&self) -> &Role { &self.role } pub const fn role_mut(&mut self) -> &mut Role { &mut self.role } pub fn role_change(&mut self, new_role: RoleTitle, at: DateTime) -> Result<(), GameError> { let mut role = new_role.title_to_role_excl_apprentice(); core::mem::swap(&mut role, &mut self.role); self.role_changes.push(RoleChange { role, new_role, changed_on_night: match at { DateTime::Day { number: _ } => return Err(GameError::NotNight), DateTime::Night { number } => number, }, }); Ok(()) } pub const fn is_wolf(&self) -> bool { self.role.wolf() } pub const fn is_village(&self) -> bool { !self.is_wolf() } pub fn night_action_prompt( &self, village: &Village, ) -> Result, GameError> { if !self.alive() || !self.role.wakes(village) { return Ok(None); } let night = match village.date_time() { DateTime::Day { number: _ } => return Err(GameError::NotNight), DateTime::Night { number } => number, }; Ok(Some(match &self.role { Role::Shapeshifter { shifted_into: Some(_), } | Role::AlphaWolf { killed: Some(_) } | Role::Militia { targeted: Some(_) } | Role::Scapegoat | Role::Villager => return Ok(None), Role::Seer => ActionPrompt::Seer { living_players: village.living_players_excluding(&self.character_id), }, Role::Arcanist => ActionPrompt::Arcanist { living_players: village.living_players_excluding(&self.character_id), }, Role::Protector { last_protected: Some(last_protected), } => ActionPrompt::Protector { targets: village.living_players_excluding(last_protected), }, Role::Protector { last_protected: None, } => ActionPrompt::Protector { targets: village.living_players_excluding(&self.character_id), }, Role::Apprentice(role) => { let current_night = match village.date_time() { DateTime::Day { number: _ } => return Ok(None), DateTime::Night { number } => number, }; return Ok(village .characters() .into_iter() .filter(|c| c.role().title() == role.title()) .filter_map(|char| char.died_to) .any(|died_to| match died_to.date_time() { DateTime::Day { number } => number.get() + 1 >= current_night, DateTime::Night { number } => number + 1 >= current_night, }) .then(|| ActionPrompt::RoleChange { new_role: role.title(), })); } Role::Elder { knows_on_night } => { let current_night = match village.date_time() { DateTime::Day { number: _ } => return Ok(None), DateTime::Night { number } => number, }; return Ok((current_night == knows_on_night.get()).then_some({ ActionPrompt::RoleChange { new_role: RoleTitle::Elder, } })); } Role::Militia { targeted: None } => ActionPrompt::Militia { living_players: village.living_players_excluding(&self.character_id), }, Role::Werewolf => ActionPrompt::WolfPackKill { living_villagers: village.living_players(), }, Role::AlphaWolf { killed: None } => ActionPrompt::AlphaWolf { living_villagers: village.living_players_excluding(&self.character_id), }, Role::DireWolf => ActionPrompt::DireWolf { living_players: village.living_players(), }, Role::Shapeshifter { shifted_into: None } => ActionPrompt::Shapeshifter, Role::Gravedigger => ActionPrompt::Gravedigger { dead_players: village.dead_targets(), }, Role::Hunter { target } => ActionPrompt::Hunter { current_target: target.as_ref().and_then(|t| village.target_by_id(t)), living_players: village.living_players_excluding(&self.character_id), }, Role::MapleWolf { last_kill_on_night } => ActionPrompt::MapleWolf { kill_or_die: last_kill_on_night + MAPLE_WOLF_ABSTAIN_LIMIT.get() == night, living_players: village.living_players_excluding(&self.character_id), }, Role::Guardian { last_protected: Some(PreviousGuardianAction::Guard(prev_target)), } => ActionPrompt::Guardian { previous: Some(PreviousGuardianAction::Guard(prev_target.clone())), living_players: village.living_players_excluding(&prev_target.character_id), }, Role::Guardian { last_protected: Some(PreviousGuardianAction::Protect(prev_target)), } => ActionPrompt::Guardian { previous: Some(PreviousGuardianAction::Protect(prev_target.clone())), living_players: village.living_players(), }, Role::Guardian { last_protected: None, } => ActionPrompt::Guardian { previous: None, living_players: village.living_players(), }, })) } }