use core::{ fmt::{Debug, Display}, num::NonZeroU8, }; use rand::distr::{Distribution, StandardUniform}; use serde::{Deserialize, Serialize}; use uuid::Uuid; use werewolves_macros::ChecksAs; use crate::{ error::GameError, message::Identification, modifier::Modifier, player::{Character, PlayerId}, role::{Role, RoleTitle}, }; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub enum OrRandom where T: Debug + Clone + PartialEq, StandardUniform: Distribution, { Determined(T), #[default] Random, } impl OrRandom where for<'a> T: Debug + Clone + PartialEq, StandardUniform: Distribution, { pub fn into_concrete(self) -> T { match self { Self::Determined(value) => value, Self::Random => rand::random(), } } } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, ChecksAs)] pub enum Category { #[checks] Wolves, Villager, Intel, Defensive, Offensive, StartsAsVillager, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChecksAs)] pub enum SetupRole { #[checks(Category::Villager)] Villager, #[checks(Category::Villager)] Scapegoat { redeemed: OrRandom }, #[checks(Category::Intel)] Seer, #[checks(Category::Intel)] Arcanist, #[checks(Category::Intel)] Gravedigger, #[checks(Category::Offensive)] Hunter, #[checks(Category::Offensive)] Militia, #[checks(Category::Offensive)] MapleWolf, #[checks(Category::Defensive)] Guardian, #[checks(Category::Defensive)] Protector, #[checks(Category::StartsAsVillager)] Apprentice { specifically: Option }, #[checks(Category::StartsAsVillager)] Elder { knows_on_night: NonZeroU8 }, #[checks(Category::Wolves)] Werewolf, #[checks(Category::Wolves)] AlphaWolf, #[checks(Category::Wolves)] DireWolf, #[checks(Category::Wolves)] Shapeshifter, } impl Display for SetupRole { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(match self { SetupRole::Villager => "Villager", SetupRole::Scapegoat { .. } => "Scapegoat", SetupRole::Seer => "Seer", SetupRole::Arcanist => "Arcanist", SetupRole::Gravedigger => "Gravedigger", SetupRole::Hunter => "Hunter", SetupRole::Militia => "Militia", SetupRole::MapleWolf => "MapleWolf", SetupRole::Guardian => "Guardian", SetupRole::Protector => "Protector", SetupRole::Apprentice { .. } => "Apprentice", SetupRole::Elder { .. } => "Elder", SetupRole::Werewolf => "Werewolf", SetupRole::AlphaWolf => "AlphaWolf", SetupRole::DireWolf => "DireWolf", SetupRole::Shapeshifter => "Shapeshifter", }) } } impl SetupRole { pub fn into_role(self, roles_in_game: &[RoleTitle]) -> Result { Ok(match self { SetupRole::Villager => Role::Villager, SetupRole::Scapegoat { redeemed } => Role::Scapegoat { redeemed: redeemed.into_concrete(), }, SetupRole::Seer => Role::Seer, SetupRole::Arcanist => Role::Arcanist, SetupRole::Gravedigger => Role::Gravedigger, SetupRole::Hunter => Role::Hunter { target: None }, SetupRole::Militia => Role::Militia { targeted: None }, SetupRole::MapleWolf => Role::MapleWolf { last_kill_on_night: 0, }, SetupRole::Guardian => Role::Guardian { last_protected: None, }, SetupRole::Protector => Role::Protector { last_protected: None, }, SetupRole::Apprentice { specifically: Some(role), } => Role::Apprentice(role), SetupRole::Apprentice { specifically: None } => { let mentors = roles_in_game .iter() .filter(|r| r.is_mentor()) .collect::>(); if mentors.is_empty() { return Err(GameError::NoApprenticeMentor); } let mentor = *mentors[rand::random_range(0..mentors.len())]; Role::Apprentice(mentor) } SetupRole::Elder { knows_on_night } => Role::Elder { knows_on_night, has_protection: true, }, SetupRole::Werewolf => Role::Werewolf, SetupRole::AlphaWolf => Role::AlphaWolf { killed: None }, SetupRole::DireWolf => Role::DireWolf, SetupRole::Shapeshifter => Role::Shapeshifter { shifted_into: None }, }) } } impl From for RoleTitle { fn from(value: SetupRole) -> Self { match value { SetupRole::Villager => RoleTitle::Villager, SetupRole::Scapegoat { .. } => RoleTitle::Scapegoat, SetupRole::Seer => RoleTitle::Seer, SetupRole::Arcanist => RoleTitle::Arcanist, SetupRole::Gravedigger => RoleTitle::Gravedigger, SetupRole::Hunter => RoleTitle::Hunter, SetupRole::Militia => RoleTitle::Militia, SetupRole::MapleWolf => RoleTitle::MapleWolf, SetupRole::Guardian => RoleTitle::Guardian, SetupRole::Protector => RoleTitle::Protector, SetupRole::Apprentice { .. } => RoleTitle::Apprentice, SetupRole::Elder { .. } => RoleTitle::Elder, SetupRole::Werewolf => RoleTitle::Werewolf, SetupRole::AlphaWolf => RoleTitle::AlphaWolf, SetupRole::DireWolf => RoleTitle::DireWolf, SetupRole::Shapeshifter => RoleTitle::Shapeshifter, } } } impl From for SetupRole { fn from(value: RoleTitle) -> Self { match value { RoleTitle::Villager => SetupRole::Villager, RoleTitle::Scapegoat => SetupRole::Scapegoat { redeemed: Default::default(), }, RoleTitle::Seer => SetupRole::Seer, RoleTitle::Arcanist => SetupRole::Arcanist, RoleTitle::Gravedigger => SetupRole::Gravedigger, RoleTitle::Hunter => SetupRole::Hunter, RoleTitle::Militia => SetupRole::Militia, RoleTitle::MapleWolf => SetupRole::MapleWolf, RoleTitle::Guardian => SetupRole::Guardian, RoleTitle::Protector => SetupRole::Protector, RoleTitle::Apprentice => SetupRole::Apprentice { specifically: None }, RoleTitle::Elder => SetupRole::Elder { knows_on_night: NonZeroU8::new(3).unwrap(), }, RoleTitle::Werewolf => SetupRole::Werewolf, RoleTitle::AlphaWolf => SetupRole::AlphaWolf, RoleTitle::DireWolf => SetupRole::DireWolf, RoleTitle::Shapeshifter => SetupRole::Shapeshifter, } } } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub struct SlotId(Uuid); impl SlotId { pub fn new() -> Self { Self(Uuid::new_v4()) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SetupSlot { pub slot_id: SlotId, pub role: SetupRole, pub modifiers: Vec, pub assign_to: Option, pub created_order: u32, } impl SetupSlot { pub fn new(title: RoleTitle, created_order: u32) -> Self { Self { created_order, assign_to: None, role: title.into(), modifiers: Vec::new(), slot_id: SlotId::new(), } } pub fn into_character( self, ident: Identification, roles_in_game: &[RoleTitle], ) -> Result { Character::new(ident.clone(), self.role.into_role(roles_in_game)?) .ok_or(GameError::PlayerNotAssignedNumber(ident.to_string())) } } impl Category { pub const fn class(&self) -> &'static str { match self { Category::Wolves => "wolves", Category::Villager => "village", Category::Intel => "intel", Category::Defensive => "defensive", Category::Offensive => "offensive", Category::StartsAsVillager => "starts-as-villager", } } }