werewolves/werewolves-proto/src/game/settings/settings_role.rs

409 lines
14 KiB
Rust

use core::{
fmt::{Debug, Display},
num::NonZeroU8,
};
use rand::distr::{Distribution, StandardUniform};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use werewolves_macros::{All, ChecksAs, Titles};
use crate::{
character::Character,
error::GameError,
message::Identification,
modifier::Modifier,
player::PlayerId,
role::{Role, RoleTitle},
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub enum OrRandom<T>
where
T: Debug + Clone + PartialEq,
StandardUniform: Distribution<T>,
{
Determined(T),
#[default]
Random,
}
impl<T> OrRandom<T>
where
for<'a> T: Debug + Clone + PartialEq,
StandardUniform: Distribution<T>,
{
pub fn into_concrete(self) -> T {
match self {
Self::Determined(value) => value,
Self::Random => rand::random(),
}
}
}
#[derive(
Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ChecksAs, All,
)]
pub enum Category {
#[checks]
Wolves,
Villager,
Intel,
Defensive,
Offensive,
StartsAsVillager,
}
impl Category {
pub fn entire_category(&self) -> Box<[SetupRoleTitle]> {
SetupRoleTitle::ALL
.iter()
.filter(|r| r.category() == *self)
.cloned()
.collect()
}
}
impl Display for Category {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Category::Wolves => "Wolves",
Category::Villager => "Villager",
Category::Intel => "Intel",
Category::Defensive => "Defensive",
Category::Offensive => "Offensive",
Category::StartsAsVillager => "Starts As Villager",
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChecksAs, Titles)]
pub enum SetupRole {
#[checks(Category::Villager)]
Villager,
#[checks(Category::Villager)]
Scapegoat { redeemed: OrRandom<bool> },
#[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 { to: Option<RoleTitle> },
#[checks(Category::StartsAsVillager)]
Elder { knows_on_night: NonZeroU8 },
#[checks(Category::Wolves)]
Werewolf,
#[checks(Category::Wolves)]
AlphaWolf,
#[checks(Category::Wolves)]
DireWolf,
#[checks(Category::Wolves)]
Shapeshifter,
#[checks(Category::Intel)]
Adjudicator,
#[checks(Category::Intel)]
PowerSeer,
#[checks(Category::Intel)]
Mortician,
#[checks(Category::Intel)]
Beholder,
#[checks(Category::Intel)]
MasonLeader { recruits_available: NonZeroU8 },
#[checks(Category::Intel)]
Empath,
#[checks(Category::Defensive)]
Vindicator,
#[checks(Category::Defensive)]
Diseased,
#[checks(Category::Defensive)]
BlackKnight,
#[checks(Category::Offensive)]
Weightlifter,
#[checks(Category::Offensive)]
PyreMaster,
}
impl SetupRoleTitle {
pub fn into_role(self) -> Role {
match self {
SetupRoleTitle::Villager => Role::Villager,
SetupRoleTitle::Scapegoat => Role::Scapegoat { redeemed: false },
SetupRoleTitle::Seer => Role::Seer,
SetupRoleTitle::Arcanist => Role::Arcanist,
SetupRoleTitle::Gravedigger => Role::Gravedigger,
SetupRoleTitle::Hunter => Role::Hunter { target: None },
SetupRoleTitle::Militia => Role::Militia { targeted: None },
SetupRoleTitle::MapleWolf => Role::MapleWolf {
last_kill_on_night: 0,
},
SetupRoleTitle::Guardian => Role::Guardian {
last_protected: None,
},
SetupRoleTitle::Protector => Role::Protector {
last_protected: None,
},
SetupRoleTitle::Apprentice => Role::Apprentice(RoleTitle::Arcanist),
SetupRoleTitle::Elder => Role::Elder {
woken_for_reveal: false,
lost_protection_night: None,
knows_on_night: NonZeroU8::new(1).unwrap(),
},
SetupRoleTitle::Werewolf => Role::Werewolf,
SetupRoleTitle::AlphaWolf => Role::AlphaWolf { killed: None },
SetupRoleTitle::DireWolf => Role::DireWolf,
SetupRoleTitle::Shapeshifter => Role::Shapeshifter { shifted_into: None },
SetupRoleTitle::Adjudicator => Role::Adjudicator,
SetupRoleTitle::PowerSeer => Role::PowerSeer,
SetupRoleTitle::Mortician => Role::Mortician,
SetupRoleTitle::Beholder => Role::Beholder,
SetupRoleTitle::MasonLeader => Role::MasonLeader {
recruits_available: 1,
recruits: Box::new([]),
},
SetupRoleTitle::Empath => Role::Empath { cursed: false },
SetupRoleTitle::Vindicator => Role::Vindicator,
SetupRoleTitle::Diseased => Role::Diseased,
SetupRoleTitle::BlackKnight => Role::BlackKnight { attacked: false },
SetupRoleTitle::Weightlifter => Role::Weightlifter,
SetupRoleTitle::PyreMaster => Role::PyreMaster {
villagers_killed: 0,
},
}
}
}
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",
SetupRole::Adjudicator => "Adjudicator",
SetupRole::PowerSeer => "PowerSeer",
SetupRole::Mortician => "Mortician",
SetupRole::Beholder => "Beholder",
SetupRole::MasonLeader { .. } => "Mason Leader",
SetupRole::Empath => "Empath",
SetupRole::Vindicator => "Vindicator",
SetupRole::Diseased => "Diseased",
SetupRole::BlackKnight => "Black Knight",
SetupRole::Weightlifter => "Weightlifter",
SetupRole::PyreMaster => "Pyremaster",
})
}
}
impl SetupRole {
pub fn into_role(self, roles_in_game: &[RoleTitle]) -> Result<Role, GameError> {
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 { to: Some(role) } => Role::Apprentice(role),
SetupRole::Apprentice { to: None } => {
let mentors = roles_in_game
.iter()
.filter(|r| r.is_mentor())
.collect::<Box<[_]>>();
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,
woken_for_reveal: false,
lost_protection_night: None,
},
SetupRole::Werewolf => Role::Werewolf,
SetupRole::AlphaWolf => Role::AlphaWolf { killed: None },
SetupRole::DireWolf => Role::DireWolf,
SetupRole::Shapeshifter => Role::Shapeshifter { shifted_into: None },
SetupRole::MasonLeader { recruits_available } => Role::MasonLeader {
recruits_available: recruits_available.get(),
recruits: Box::new([]),
},
SetupRole::Adjudicator => Role::Adjudicator,
SetupRole::PowerSeer => Role::PowerSeer,
SetupRole::Mortician => Role::Mortician,
SetupRole::Beholder => Role::Beholder,
SetupRole::Empath => Role::Empath { cursed: false },
SetupRole::Vindicator => Role::Vindicator,
SetupRole::Diseased => Role::Diseased,
SetupRole::BlackKnight => Role::BlackKnight { attacked: false },
SetupRole::Weightlifter => Role::Weightlifter,
SetupRole::PyreMaster => Role::PyreMaster {
villagers_killed: 0,
},
})
}
}
impl From<SetupRole> 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,
SetupRole::Adjudicator => RoleTitle::Adjudicator,
SetupRole::PowerSeer => RoleTitle::PowerSeer,
SetupRole::Mortician => RoleTitle::Mortician,
SetupRole::Beholder => RoleTitle::Beholder,
SetupRole::MasonLeader { .. } => RoleTitle::MasonLeader,
SetupRole::Empath => RoleTitle::Empath,
SetupRole::Vindicator => RoleTitle::Vindicator,
SetupRole::Diseased => RoleTitle::Diseased,
SetupRole::BlackKnight => RoleTitle::BlackKnight,
SetupRole::Weightlifter => RoleTitle::Weightlifter,
SetupRole::PyreMaster => RoleTitle::PyreMaster,
}
}
}
impl From<RoleTitle> 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 { to: 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,
RoleTitle::Adjudicator => SetupRole::Adjudicator,
RoleTitle::PowerSeer => SetupRole::PowerSeer,
RoleTitle::Mortician => SetupRole::Mortician,
RoleTitle::Beholder => SetupRole::Beholder,
RoleTitle::MasonLeader => SetupRole::MasonLeader {
recruits_available: NonZeroU8::new(1).unwrap(),
},
RoleTitle::Empath => SetupRole::Empath,
RoleTitle::Vindicator => SetupRole::Vindicator,
RoleTitle::Diseased => SetupRole::Diseased,
RoleTitle::BlackKnight => SetupRole::BlackKnight,
RoleTitle::Weightlifter => SetupRole::Weightlifter,
RoleTitle::PyreMaster => SetupRole::PyreMaster,
}
}
}
#[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<Modifier>,
pub assign_to: Option<PlayerId>,
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, GameError> {
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",
}
}
}