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

262 lines
8.3 KiB
Rust
Raw Normal View History

2025-10-04 17:50:29 +01:00
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<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, 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<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 { specifically: 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,
}
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<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 {
specifically: Some(role),
} => Role::Apprentice(role),
SetupRole::Apprentice { specifically: 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,
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<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,
}
}
}
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 { 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<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",
}
}
}