184 lines
5.1 KiB
Rust
184 lines
5.1 KiB
Rust
|
|
use core::num::NonZeroU8;
|
||
|
|
|
||
|
|
use serde::{Deserialize, Serialize};
|
||
|
|
use werewolves_macros::{ChecksAs, Titles};
|
||
|
|
|
||
|
|
use crate::{
|
||
|
|
game::{DateTime, Village},
|
||
|
|
message::Target,
|
||
|
|
player::CharacterId,
|
||
|
|
};
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ChecksAs, Titles)]
|
||
|
|
pub enum Role {
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
Villager,
|
||
|
|
#[checks(Alignment::Wolves)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
Scapegoat,
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Seer,
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Arcanist,
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Gravedigger,
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
#[checks]
|
||
|
|
Hunter { target: Option<CharacterId> },
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Militia { targeted: Option<CharacterId> },
|
||
|
|
#[checks(Alignment::Wolves)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
MapleWolf { last_kill_on_night: u8 },
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Guardian {
|
||
|
|
last_protected: Option<PreviousGuardianAction>,
|
||
|
|
},
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Protector { last_protected: Option<CharacterId> },
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
Apprentice(Box<Role>),
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("is_mentor")]
|
||
|
|
Elder { knows_on_night: NonZeroU8 },
|
||
|
|
|
||
|
|
#[checks(Alignment::Wolves)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("wolf")]
|
||
|
|
Werewolf,
|
||
|
|
#[checks(Alignment::Wolves)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("wolf")]
|
||
|
|
AlphaWolf { killed: Option<CharacterId> },
|
||
|
|
#[checks(Alignment::Village)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("wolf")]
|
||
|
|
DireWolf,
|
||
|
|
#[checks(Alignment::Wolves)]
|
||
|
|
#[checks("killer")]
|
||
|
|
#[checks("powerful")]
|
||
|
|
#[checks("wolf")]
|
||
|
|
Shapeshifter { shifted_into: Option<CharacterId> },
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Role {
|
||
|
|
/// [RoleTitle] as shown to the player on role assignment
|
||
|
|
pub const fn initial_shown_role(&self) -> RoleTitle {
|
||
|
|
match self {
|
||
|
|
Role::Apprentice(_) | Role::Elder { knows_on_night: _ } => RoleTitle::Villager,
|
||
|
|
_ => self.title(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn wakes(&self, village: &Village) -> bool {
|
||
|
|
let night_zero = match village.date_time() {
|
||
|
|
DateTime::Day { number: _ } => return false,
|
||
|
|
DateTime::Night { number } => number == 0,
|
||
|
|
};
|
||
|
|
if night_zero {
|
||
|
|
return match self {
|
||
|
|
Role::DireWolf | Role::Arcanist | Role::Seer => true,
|
||
|
|
|
||
|
|
Role::Shapeshifter { shifted_into: _ }
|
||
|
|
| Role::Werewolf
|
||
|
|
| Role::AlphaWolf { killed: _ }
|
||
|
|
| Role::Elder { knows_on_night: _ }
|
||
|
|
| Role::Gravedigger
|
||
|
|
| Role::Hunter { target: _ }
|
||
|
|
| Role::Militia { targeted: _ }
|
||
|
|
| Role::MapleWolf {
|
||
|
|
last_kill_on_night: _,
|
||
|
|
}
|
||
|
|
| Role::Guardian { last_protected: _ }
|
||
|
|
| Role::Apprentice(_)
|
||
|
|
| Role::Villager
|
||
|
|
| Role::Scapegoat
|
||
|
|
| Role::Protector { last_protected: _ } => false,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
match self {
|
||
|
|
Role::AlphaWolf { killed: Some(_) }
|
||
|
|
| Role::Werewolf
|
||
|
|
| Role::Scapegoat
|
||
|
|
| Role::Militia { targeted: Some(_) }
|
||
|
|
| Role::Villager => false,
|
||
|
|
|
||
|
|
Role::Shapeshifter { shifted_into: _ }
|
||
|
|
| Role::DireWolf
|
||
|
|
| Role::AlphaWolf { killed: None }
|
||
|
|
| Role::Arcanist
|
||
|
|
| Role::Protector { last_protected: _ }
|
||
|
|
| Role::Gravedigger
|
||
|
|
| Role::Hunter { target: _ }
|
||
|
|
| Role::Militia { targeted: None }
|
||
|
|
| Role::MapleWolf {
|
||
|
|
last_kill_on_night: _,
|
||
|
|
}
|
||
|
|
| Role::Guardian { last_protected: _ }
|
||
|
|
| Role::Seer => true,
|
||
|
|
|
||
|
|
Role::Apprentice(role) => village
|
||
|
|
.characters()
|
||
|
|
.iter()
|
||
|
|
.any(|c| c.role().title() == role.title()),
|
||
|
|
|
||
|
|
Role::Elder { knows_on_night } => match village.date_time() {
|
||
|
|
DateTime::Night { number } => number == knows_on_night.get(),
|
||
|
|
_ => false,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||
|
|
pub enum Alignment {
|
||
|
|
Village,
|
||
|
|
Wolves,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Serialize, Deserialize, ChecksAs)]
|
||
|
|
pub enum ArcanistCheck {
|
||
|
|
#[checks]
|
||
|
|
Same,
|
||
|
|
#[checks]
|
||
|
|
Different,
|
||
|
|
}
|
||
|
|
|
||
|
|
pub const MAPLE_WOLF_ABSTAIN_LIMIT: NonZeroU8 = NonZeroU8::new(3).unwrap();
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
|
|
pub enum RoleBlock {
|
||
|
|
Direwolf,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||
|
|
pub enum PreviousGuardianAction {
|
||
|
|
Protect(Target),
|
||
|
|
Guard(Target),
|
||
|
|
}
|