2025-10-13 22:15:40 +01:00
|
|
|
use core::{fmt::Display, num::NonZeroU8, ops::Not};
|
2025-06-23 09:48:28 +01:00
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use werewolves_macros::{ChecksAs, Titles};
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2025-10-06 20:45:15 +01:00
|
|
|
character::CharacterId,
|
2025-10-06 21:59:44 +01:00
|
|
|
diedto::DiedTo,
|
2025-10-12 23:48:52 +01:00
|
|
|
game::{GameTime, Village},
|
2025-10-02 17:52:12 +01:00
|
|
|
message::CharacterIdentity,
|
2025-06-23 09:48:28 +01:00
|
|
|
};
|
|
|
|
|
|
2025-10-12 23:48:52 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Default)]
|
|
|
|
|
pub enum Killer {
|
|
|
|
|
Killer,
|
|
|
|
|
#[default]
|
|
|
|
|
NotKiller,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Killer {
|
|
|
|
|
pub const fn killer(&self) -> bool {
|
|
|
|
|
matches!(self, Killer::Killer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Killer {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Killer::Killer => f.write_str("Killer"),
|
|
|
|
|
Killer::NotKiller => f.write_str("Not a Killer"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Not for Killer {
|
|
|
|
|
type Output = Killer;
|
|
|
|
|
|
|
|
|
|
fn not(self) -> Self::Output {
|
|
|
|
|
match self {
|
|
|
|
|
Killer::Killer => Killer::NotKiller,
|
|
|
|
|
Killer::NotKiller => Killer::Killer,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Default)]
|
|
|
|
|
pub enum Powerful {
|
|
|
|
|
Powerful,
|
|
|
|
|
#[default]
|
|
|
|
|
NotPowerful,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Powerful {
|
|
|
|
|
pub const fn powerful(&self) -> bool {
|
|
|
|
|
matches!(self, Powerful::Powerful)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Powerful {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Powerful::Powerful => f.write_str("Powerful"),
|
|
|
|
|
Powerful::NotPowerful => f.write_str("Not Powerful"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Not for Powerful {
|
|
|
|
|
type Output = Powerful;
|
|
|
|
|
|
|
|
|
|
fn not(self) -> Self::Output {
|
|
|
|
|
match self {
|
|
|
|
|
Powerful::Powerful => Powerful::NotPowerful,
|
|
|
|
|
Powerful::NotPowerful => Powerful::Powerful,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
|
pub enum AlignmentEq {
|
|
|
|
|
Same,
|
|
|
|
|
Different,
|
|
|
|
|
}
|
|
|
|
|
impl Not for AlignmentEq {
|
|
|
|
|
type Output = AlignmentEq;
|
|
|
|
|
|
|
|
|
|
fn not(self) -> Self::Output {
|
|
|
|
|
match self {
|
|
|
|
|
AlignmentEq::Same => Self::Different,
|
|
|
|
|
AlignmentEq::Different => Self::Same,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl AlignmentEq {
|
|
|
|
|
pub const fn new(same: bool) -> Self {
|
|
|
|
|
match same {
|
|
|
|
|
true => Self::Same,
|
|
|
|
|
false => Self::Different,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub const fn same(&self) -> bool {
|
|
|
|
|
matches!(self, Self::Same)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-05 10:52:37 +01:00
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, ChecksAs, Titles)]
|
2025-06-23 09:48:28 +01:00
|
|
|
pub enum Role {
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::NotKiller)]
|
|
|
|
|
#[checks(Powerful::NotPowerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
Villager,
|
|
|
|
|
#[checks(Alignment::Wolves)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-10-04 17:50:29 +01:00
|
|
|
Scapegoat { redeemed: bool },
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Seer,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Arcanist,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
2025-10-06 20:45:15 +01:00
|
|
|
Adjudicator,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
PowerSeer,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Mortician,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Beholder,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
MasonLeader {
|
|
|
|
|
recruits_available: u8,
|
|
|
|
|
recruits: Box<[CharacterId]>,
|
|
|
|
|
},
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
Empath { cursed: bool },
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Vindicator,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Diseased,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
2025-10-06 21:59:44 +01:00
|
|
|
BlackKnight { attacked: Option<DiedTo> },
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Weightlifter,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::Killer)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
PyreMaster { villagers_killed: u8 },
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-06 20:45:15 +01:00
|
|
|
#[checks("is_mentor")]
|
2025-06-23 09:48:28 +01:00
|
|
|
Gravedigger,
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
#[checks]
|
|
|
|
|
Hunter { target: Option<CharacterId> },
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Militia { targeted: Option<CharacterId> },
|
|
|
|
|
#[checks(Alignment::Wolves)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
MapleWolf { last_kill_on_night: u8 },
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::Killer)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Guardian {
|
|
|
|
|
last_protected: Option<PreviousGuardianAction>,
|
|
|
|
|
},
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
|
|
|
|
Protector { last_protected: Option<CharacterId> },
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-04 17:50:29 +01:00
|
|
|
Apprentice(RoleTitle),
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("is_mentor")]
|
2025-10-04 17:50:29 +01:00
|
|
|
Elder {
|
|
|
|
|
knows_on_night: NonZeroU8,
|
2025-10-05 10:52:37 +01:00
|
|
|
woken_for_reveal: bool,
|
|
|
|
|
lost_protection_night: Option<NonZeroU8>,
|
2025-10-04 17:50:29 +01:00
|
|
|
},
|
2025-10-07 17:45:21 +01:00
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Powerful::Powerful)]
|
|
|
|
|
#[checks(Killer::NotKiller)]
|
2025-10-07 17:45:21 +01:00
|
|
|
Insomniac,
|
2025-06-23 09:48:28 +01:00
|
|
|
|
|
|
|
|
#[checks(Alignment::Wolves)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("wolf")]
|
|
|
|
|
Werewolf,
|
|
|
|
|
#[checks(Alignment::Wolves)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("wolf")]
|
|
|
|
|
AlphaWolf { killed: Option<CharacterId> },
|
|
|
|
|
#[checks(Alignment::Village)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("wolf")]
|
2025-10-13 23:29:10 +01:00
|
|
|
DireWolf { last_blocked: Option<CharacterId> },
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks(Alignment::Wolves)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-06-23 09:48:28 +01:00
|
|
|
#[checks("wolf")]
|
|
|
|
|
Shapeshifter { shifted_into: Option<CharacterId> },
|
2025-10-07 02:52:06 +01:00
|
|
|
#[checks(Alignment::Wolves)]
|
2025-10-12 23:48:52 +01:00
|
|
|
#[checks(Killer::Killer)]
|
|
|
|
|
#[checks(Powerful::Powerful)]
|
2025-10-07 02:52:06 +01:00
|
|
|
#[checks("wolf")]
|
|
|
|
|
LoneWolf,
|
2025-06-23 09:48:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Role {
|
|
|
|
|
/// [RoleTitle] as shown to the player on role assignment
|
|
|
|
|
pub const fn initial_shown_role(&self) -> RoleTitle {
|
|
|
|
|
match self {
|
2025-10-07 17:45:21 +01:00
|
|
|
Role::Apprentice(_) | Role::Elder { .. } | Role::Insomniac => RoleTitle::Villager,
|
2025-06-23 09:48:28 +01:00
|
|
|
_ => self.title(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 23:48:52 +01:00
|
|
|
pub const fn killing_wolf_order(&self) -> Option<KillingWolfOrder> {
|
|
|
|
|
Some(match self {
|
|
|
|
|
Role::Villager
|
|
|
|
|
| Role::Scapegoat { .. }
|
|
|
|
|
| Role::Seer
|
|
|
|
|
| Role::Arcanist
|
|
|
|
|
| Role::Adjudicator
|
|
|
|
|
| Role::PowerSeer
|
|
|
|
|
| Role::Mortician
|
|
|
|
|
| Role::Beholder
|
|
|
|
|
| Role::MasonLeader { .. }
|
|
|
|
|
| Role::Empath { .. }
|
|
|
|
|
| Role::Vindicator
|
|
|
|
|
| Role::Diseased
|
|
|
|
|
| Role::BlackKnight { .. }
|
|
|
|
|
| Role::Weightlifter
|
|
|
|
|
| Role::PyreMaster { .. }
|
|
|
|
|
| Role::Gravedigger
|
|
|
|
|
| Role::Hunter { .. }
|
|
|
|
|
| Role::Militia { .. }
|
|
|
|
|
| Role::MapleWolf { .. }
|
|
|
|
|
| Role::Guardian { .. }
|
|
|
|
|
| Role::Protector { .. }
|
|
|
|
|
| Role::Apprentice(..)
|
|
|
|
|
| Role::Elder { .. }
|
|
|
|
|
| Role::Insomniac => return None,
|
|
|
|
|
|
|
|
|
|
Role::Werewolf => KillingWolfOrder::Werewolf,
|
|
|
|
|
Role::AlphaWolf { .. } => KillingWolfOrder::AlphaWolf,
|
2025-10-13 23:29:10 +01:00
|
|
|
Role::DireWolf { .. } => KillingWolfOrder::DireWolf,
|
2025-10-12 23:48:52 +01:00
|
|
|
Role::Shapeshifter { .. } => KillingWolfOrder::Shapeshifter,
|
|
|
|
|
Role::LoneWolf => KillingWolfOrder::LoneWolf,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 01:03:16 +01:00
|
|
|
pub const fn wakes_night_zero(&self) -> bool {
|
|
|
|
|
match self {
|
2025-10-13 22:15:40 +01:00
|
|
|
Role::PowerSeer
|
2025-10-06 20:45:15 +01:00
|
|
|
| Role::Adjudicator
|
2025-10-13 23:29:10 +01:00
|
|
|
| Role::DireWolf { .. }
|
2025-10-06 20:45:15 +01:00
|
|
|
| Role::Arcanist
|
|
|
|
|
| Role::Seer => true,
|
2025-10-06 01:03:16 +01:00
|
|
|
|
2025-10-13 22:15:40 +01:00
|
|
|
Role::Insomniac // has to at least get one good night of sleep, right?
|
|
|
|
|
| Role::Beholder
|
|
|
|
|
| Role::LoneWolf
|
2025-10-07 02:52:06 +01:00
|
|
|
| Role::Shapeshifter { .. }
|
2025-10-06 01:03:16 +01:00
|
|
|
| Role::Werewolf
|
|
|
|
|
| Role::AlphaWolf { .. }
|
|
|
|
|
| Role::Elder { .. }
|
|
|
|
|
| Role::Gravedigger
|
|
|
|
|
| Role::Hunter { .. }
|
|
|
|
|
| Role::Militia { .. }
|
|
|
|
|
| Role::MapleWolf { .. }
|
|
|
|
|
| Role::Guardian { .. }
|
|
|
|
|
| Role::Apprentice(_)
|
|
|
|
|
| Role::Villager
|
|
|
|
|
| Role::Scapegoat { .. }
|
2025-10-06 20:45:15 +01:00
|
|
|
| Role::Mortician
|
|
|
|
|
| Role::MasonLeader { .. }
|
|
|
|
|
| Role::Empath { .. }
|
|
|
|
|
| Role::Vindicator
|
|
|
|
|
| Role::Diseased
|
|
|
|
|
| Role::BlackKnight { .. }
|
|
|
|
|
| Role::Weightlifter
|
|
|
|
|
| Role::PyreMaster { .. }
|
2025-10-06 01:03:16 +01:00
|
|
|
| Role::Protector { .. } => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 09:48:28 +01:00
|
|
|
pub fn wakes(&self, village: &Village) -> bool {
|
2025-10-12 23:48:52 +01:00
|
|
|
let night_zero = match village.time() {
|
|
|
|
|
GameTime::Day { number: _ } => return false,
|
|
|
|
|
GameTime::Night { number } => number == 0,
|
2025-06-23 09:48:28 +01:00
|
|
|
};
|
|
|
|
|
if night_zero {
|
2025-10-06 01:03:16 +01:00
|
|
|
return self.wakes_night_zero();
|
2025-06-23 09:48:28 +01:00
|
|
|
}
|
|
|
|
|
match self {
|
|
|
|
|
Role::AlphaWolf { killed: Some(_) }
|
|
|
|
|
| Role::Werewolf
|
2025-10-04 17:50:29 +01:00
|
|
|
| Role::Scapegoat { redeemed: false }
|
2025-06-23 09:48:28 +01:00
|
|
|
| Role::Militia { targeted: Some(_) }
|
2025-10-06 20:45:15 +01:00
|
|
|
| Role::Diseased
|
|
|
|
|
| Role::BlackKnight { .. }
|
2025-06-23 09:48:28 +01:00
|
|
|
| Role::Villager => false,
|
|
|
|
|
|
2025-10-12 23:48:52 +01:00
|
|
|
Role::LoneWolf => match village.time() {
|
|
|
|
|
GameTime::Day { number: _ } => return false,
|
|
|
|
|
GameTime::Night { number } => NonZeroU8::new(number),
|
2025-10-07 02:52:06 +01:00
|
|
|
}
|
|
|
|
|
.map(|night| village.executions_on_day(night))
|
|
|
|
|
.map(|execs| execs.iter().any(|e| e.is_wolf()))
|
|
|
|
|
.unwrap_or_default(),
|
|
|
|
|
|
2025-10-07 17:45:21 +01:00
|
|
|
Role::Insomniac
|
|
|
|
|
| Role::PowerSeer
|
2025-10-06 20:45:15 +01:00
|
|
|
| Role::Mortician
|
|
|
|
|
| Role::Beholder
|
|
|
|
|
| Role::MasonLeader { .. }
|
|
|
|
|
| Role::Empath { .. }
|
|
|
|
|
| Role::Vindicator
|
|
|
|
|
| Role::Weightlifter
|
|
|
|
|
| Role::PyreMaster { .. }
|
|
|
|
|
| Role::Adjudicator
|
|
|
|
|
| Role::Scapegoat { redeemed: true }
|
2025-10-04 17:50:29 +01:00
|
|
|
| Role::Shapeshifter { .. }
|
2025-10-13 23:29:10 +01:00
|
|
|
| Role::DireWolf { .. }
|
2025-06-23 09:48:28 +01:00
|
|
|
| Role::AlphaWolf { killed: None }
|
|
|
|
|
| Role::Arcanist
|
2025-10-04 17:50:29 +01:00
|
|
|
| Role::Protector { .. }
|
2025-06-23 09:48:28 +01:00
|
|
|
| Role::Gravedigger
|
2025-10-04 17:50:29 +01:00
|
|
|
| Role::Hunter { .. }
|
2025-06-23 09:48:28 +01:00
|
|
|
| Role::Militia { targeted: None }
|
2025-10-04 17:50:29 +01:00
|
|
|
| Role::MapleWolf { .. }
|
|
|
|
|
| Role::Guardian { .. }
|
2025-06-23 09:48:28 +01:00
|
|
|
| Role::Seer => true,
|
|
|
|
|
|
2025-10-04 17:50:29 +01:00
|
|
|
Role::Apprentice(title) => village
|
2025-06-23 09:48:28 +01:00
|
|
|
.characters()
|
|
|
|
|
.iter()
|
2025-10-06 20:45:15 +01:00
|
|
|
.any(|c| c.role_title() == *title),
|
2025-06-23 09:48:28 +01:00
|
|
|
|
2025-10-05 10:52:37 +01:00
|
|
|
Role::Elder {
|
|
|
|
|
knows_on_night,
|
|
|
|
|
woken_for_reveal,
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
!woken_for_reveal
|
2025-10-12 23:48:52 +01:00
|
|
|
&& match village.time() {
|
|
|
|
|
GameTime::Night { number } => number == knows_on_night.get(),
|
2025-10-05 10:52:37 +01:00
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-23 09:48:28 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-14 17:51:25 +01:00
|
|
|
impl RoleTitle {
|
|
|
|
|
pub fn falsely_appear_village() -> Box<[RoleTitle]> {
|
|
|
|
|
Self::ALL
|
|
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.filter(|r| r.wolf() && r.alignment().village())
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn falsely_appear_wolf() -> Box<[RoleTitle]> {
|
|
|
|
|
Self::ALL
|
|
|
|
|
.iter()
|
|
|
|
|
.copied()
|
|
|
|
|
.filter(|r| !r.wolf() && r.alignment().wolves())
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 23:48:52 +01:00
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
2025-06-23 09:48:28 +01:00
|
|
|
pub enum Alignment {
|
|
|
|
|
Village,
|
|
|
|
|
Wolves,
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-14 17:51:25 +01:00
|
|
|
impl Alignment {
|
|
|
|
|
pub const fn village(&self) -> bool {
|
|
|
|
|
matches!(self, Alignment::Village)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub const fn wolves(&self) -> bool {
|
|
|
|
|
matches!(self, Alignment::Wolves)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 23:48:52 +01:00
|
|
|
impl Display for Alignment {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
Alignment::Village => f.write_str("Village"),
|
|
|
|
|
Alignment::Wolves => f.write_str("Wolves"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 09:48:28 +01:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ChecksAs)]
|
|
|
|
|
pub enum ArcanistCheck {
|
|
|
|
|
#[checks]
|
|
|
|
|
Same,
|
|
|
|
|
#[checks]
|
|
|
|
|
Different,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub const MAPLE_WOLF_ABSTAIN_LIMIT: NonZeroU8 = NonZeroU8::new(3).unwrap();
|
2025-10-06 22:30:01 +01:00
|
|
|
pub const PYREMASTER_VILLAGER_KILLS_TO_DIE: NonZeroU8 = NonZeroU8::new(2).unwrap();
|
2025-06-23 09:48:28 +01:00
|
|
|
|
2025-10-07 21:18:31 +01:00
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
2025-06-23 09:48:28 +01:00
|
|
|
pub enum RoleBlock {
|
|
|
|
|
Direwolf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
|
|
|
|
pub enum PreviousGuardianAction {
|
2025-10-02 17:52:12 +01:00
|
|
|
Protect(CharacterIdentity),
|
|
|
|
|
Guard(CharacterIdentity),
|
2025-06-23 09:48:28 +01:00
|
|
|
}
|
2025-10-12 23:48:52 +01:00
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|
|
|
|
pub enum KillingWolfOrder {
|
|
|
|
|
Werewolf,
|
|
|
|
|
AlphaWolf,
|
|
|
|
|
Shapeshifter,
|
|
|
|
|
Berserker,
|
|
|
|
|
Psion,
|
|
|
|
|
Bloodletter,
|
|
|
|
|
Bloodhound,
|
|
|
|
|
DireWolf,
|
|
|
|
|
LoneWolf,
|
|
|
|
|
}
|