proto changes to accomodate leptos client
This commit is contained in:
parent
78ecb6c164
commit
314e113a46
|
|
@ -47,6 +47,12 @@ impl CharacterId {
|
|||
pub const fn from_u128(v: u128) -> Self {
|
||||
Self(uuid::Uuid::from_u128(v))
|
||||
}
|
||||
pub const fn from_uuid(v: uuid::Uuid) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
pub const fn into_uuid(self) -> uuid::Uuid {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CharacterId {
|
||||
|
|
@ -66,7 +72,11 @@ pub struct Character {
|
|||
}
|
||||
|
||||
impl Character {
|
||||
pub fn new(
|
||||
pub fn new(ident: Identification, role: Role, auras: Vec<Aura>) -> Option<Self> {
|
||||
Self::new_with_character_id(ident, role, auras, CharacterId::new())
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_character_id(
|
||||
Identification {
|
||||
player_id,
|
||||
public:
|
||||
|
|
@ -78,6 +88,7 @@ impl Character {
|
|||
}: Identification,
|
||||
role: Role,
|
||||
auras: Vec<Aura>,
|
||||
character_id: CharacterId,
|
||||
) -> Option<Self> {
|
||||
Some(Self {
|
||||
role,
|
||||
|
|
@ -86,7 +97,7 @@ impl Character {
|
|||
auras: Auras::new(auras),
|
||||
role_changes: Vec::new(),
|
||||
identity: CharacterIdentity {
|
||||
character_id: CharacterId::new(),
|
||||
character_id,
|
||||
name,
|
||||
pronouns,
|
||||
number: number?,
|
||||
|
|
|
|||
|
|
@ -109,4 +109,16 @@ pub enum GameError {
|
|||
NoCurrentPromptForAura,
|
||||
#[error("you're not dead")]
|
||||
NotDead,
|
||||
#[error("invalid character id assignment for player ID {for_player}")]
|
||||
InvalidCharacterIdAssignment { for_player: PlayerId },
|
||||
#[error("already joined")]
|
||||
AlreadyJoined,
|
||||
#[error("cannot join own game")]
|
||||
CannotJoinOwnGame,
|
||||
#[error("cannot leave a started game")]
|
||||
CannotLeaveOnceStarted,
|
||||
#[error("cannot join a started game")]
|
||||
CannotJoinStartedGame,
|
||||
#[error("game already started")]
|
||||
GameAlreadyStarted,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,19 @@ pub struct Game {
|
|||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new_with_assigned_character_ids(
|
||||
players: &[(Identification, CharacterId)],
|
||||
settings: GameSettings,
|
||||
) -> Result<Self> {
|
||||
let village = Village::new_with_assigned_character_ids(players, settings)?;
|
||||
Ok(Self {
|
||||
started: Utc::now(),
|
||||
history: GameStory::new(village.clone()),
|
||||
state: GameState::Night {
|
||||
night: Night::new(village)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
pub fn new(players: &[Identification], settings: GameSettings) -> Result<Self> {
|
||||
let village = Village::new(players, settings)?;
|
||||
Ok(Self {
|
||||
|
|
|
|||
|
|
@ -829,7 +829,7 @@ impl Night {
|
|||
NightChange::Protection {
|
||||
target,
|
||||
protection: _,
|
||||
} => target == kill_target,
|
||||
} => target == kill_target || target == *source,
|
||||
_ => false,
|
||||
}) {
|
||||
// there is protection, so the kill doesn't happen -> no shapeshift
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ use super::Result;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{character::Character, error::GameError, message::Identification, role::RoleTitle};
|
||||
use crate::{
|
||||
character::{Character, CharacterId},
|
||||
error::GameError,
|
||||
message::Identification,
|
||||
role::RoleTitle,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct GameSettings {
|
||||
|
|
@ -115,8 +120,12 @@ impl GameSettings {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn assign(&self, players: &[Identification]) -> Result<Box<[Character]>> {
|
||||
self.check_with_player_list(players)?;
|
||||
pub fn assign_with_set_character_ids(
|
||||
&self,
|
||||
players: &[(Identification, CharacterId)],
|
||||
) -> Result<Box<[Character]>> {
|
||||
let idents_only = players.iter().map(|i| i.0.clone()).collect::<Box<_>>();
|
||||
self.check_with_player_list(&idents_only)?;
|
||||
|
||||
let roles_in_game = self
|
||||
.roles
|
||||
|
|
@ -131,7 +140,7 @@ impl GameSettings {
|
|||
s.assign_to.as_ref().map(|assign_to| {
|
||||
players
|
||||
.iter()
|
||||
.find(|pid| pid.player_id == *assign_to)
|
||||
.find(|(pid, _)| pid.player_id == *assign_to)
|
||||
.ok_or(GameError::AssignedPlayerMissing(*assign_to))
|
||||
.map(|id| (id, s))
|
||||
})
|
||||
|
|
@ -140,10 +149,10 @@ impl GameSettings {
|
|||
|
||||
let mut random_assign_players = players
|
||||
.iter()
|
||||
.filter(|p| {
|
||||
.filter(|(p, _)| {
|
||||
!with_assigned_roles
|
||||
.iter()
|
||||
.any(|(r, _)| r.player_id == p.player_id)
|
||||
.any(|((r, _), _)| r.player_id == p.player_id)
|
||||
})
|
||||
.collect::<Box<[_]>>();
|
||||
|
||||
|
|
@ -156,10 +165,21 @@ impl GameSettings {
|
|||
.into_iter()
|
||||
.zip(self.roles.iter().filter(|s| s.assign_to.is_none())),
|
||||
)
|
||||
.map(|(id, slot)| slot.clone().into_character(id.clone(), &roles_in_game))
|
||||
.map(|((ident, char_id), slot)| {
|
||||
slot.clone()
|
||||
.into_character_with_id(ident.clone(), &roles_in_game, *char_id)
|
||||
})
|
||||
.collect::<Result<Box<[_]>>>()
|
||||
}
|
||||
|
||||
pub fn assign(&self, players: &[Identification]) -> Result<Box<[Character]>> {
|
||||
let with_cids = players
|
||||
.iter()
|
||||
.map(|ident| (ident.clone(), CharacterId::new()))
|
||||
.collect::<Box<_>>();
|
||||
self.assign_with_set_character_ids(&with_cids)
|
||||
}
|
||||
|
||||
pub fn check_with_player_list(&self, players: &[Identification]) -> Result<()> {
|
||||
self.check()?;
|
||||
let (p_len, r_len) = (players.len(), self.roles.len());
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use werewolves_macros::{All, ChecksAs, Titles};
|
|||
|
||||
use crate::{
|
||||
aura::AuraTitle,
|
||||
character::Character,
|
||||
character::{Character, CharacterId},
|
||||
error::GameError,
|
||||
message::Identification,
|
||||
player::PlayerId,
|
||||
|
|
@ -435,6 +435,12 @@ impl SlotId {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for SlotId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct SetupSlot {
|
||||
pub slot_id: SlotId,
|
||||
|
|
@ -470,17 +476,22 @@ impl SetupSlot {
|
|||
)
|
||||
.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",
|
||||
}
|
||||
pub fn into_character_with_id(
|
||||
self,
|
||||
ident: Identification,
|
||||
roles_in_game: &[RoleTitle],
|
||||
id: CharacterId,
|
||||
) -> Result<Character, GameError> {
|
||||
Character::new_with_character_id(
|
||||
ident.clone(),
|
||||
self.role.into_role(roles_in_game)?,
|
||||
self.auras
|
||||
.into_iter()
|
||||
.map(|aura| aura.into_aura())
|
||||
.collect(),
|
||||
id,
|
||||
)
|
||||
.ok_or(GameError::PlayerNotAssignedNumber(ident.to_string()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,27 @@ pub struct Village {
|
|||
}
|
||||
|
||||
impl Village {
|
||||
pub fn new_with_assigned_character_ids(
|
||||
players: &[(Identification, CharacterId)],
|
||||
settings: GameSettings,
|
||||
) -> Result<Self> {
|
||||
if settings.min_players_needed() > players.len() {
|
||||
return Err(GameError::TooManyRoles {
|
||||
players: players.len() as u8,
|
||||
roles: settings.min_players_needed() as u8,
|
||||
});
|
||||
}
|
||||
let mut characters = settings.assign_with_set_character_ids(players)?;
|
||||
assert_eq!(characters.len(), players.len());
|
||||
characters.sort_by_key(|l| l.number());
|
||||
|
||||
Ok(Self {
|
||||
settings,
|
||||
characters,
|
||||
time: GameTime::Night { number: 0 },
|
||||
dead_chat: DeadChat::new(),
|
||||
})
|
||||
}
|
||||
pub fn new(players: &[Identification], settings: GameSettings) -> Result<Self> {
|
||||
if settings.min_players_needed() > players.len() {
|
||||
return Err(GameError::TooManyRoles {
|
||||
|
|
|
|||
|
|
@ -1119,7 +1119,7 @@ fn big_game_test_based_on_story_test() {
|
|||
);
|
||||
|
||||
game.execute().title().vindicator();
|
||||
game.mark(game.character_by_player_id(shapeshifter).character_id());
|
||||
game.mark(game.character_by_player_id(insomniac).character_id());
|
||||
game.r#continue().sleep();
|
||||
|
||||
game.next().title().wolf_pack_kill();
|
||||
|
|
@ -1127,11 +1127,16 @@ fn big_game_test_based_on_story_test() {
|
|||
game.r#continue().r#continue();
|
||||
|
||||
game.next().title().shapeshifter();
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::Shapeshift,
|
||||
)))
|
||||
.expect("shapeshift");
|
||||
// game.r#continue().r#continue();
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::Shapeshift,
|
||||
)))
|
||||
.expect("shapeshift"),
|
||||
ServerToHostMessage::ActionResult(
|
||||
Some(game.character_by_player_id(shapeshifter).identity()),
|
||||
ActionResult::Continue
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
game.next(),
|
||||
|
|
|
|||
|
|
@ -444,3 +444,52 @@ fn shapeshift_removes_village_prompt_but_previous_can_bring_it_back() {
|
|||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shapeshifter_protected_when_shifting_prevents_shift() {
|
||||
init_log();
|
||||
let players = gen_players(1..21);
|
||||
let mut player_ids = players.iter().map(|p| p.player_id);
|
||||
let shapeshifter = player_ids.next().unwrap();
|
||||
let wolf = player_ids.next().unwrap();
|
||||
let protector = player_ids.next().unwrap();
|
||||
let hunter = player_ids.next().unwrap();
|
||||
let mut settings = GameSettings::empty();
|
||||
settings.add_and_assign(SetupRole::Shapeshifter, shapeshifter);
|
||||
settings.add_and_assign(SetupRole::Werewolf, wolf);
|
||||
settings.add_and_assign(SetupRole::Protector, protector);
|
||||
settings.add_and_assign(SetupRole::Hunter, hunter);
|
||||
settings.fill_remaining_slots_with_villagers(players.len());
|
||||
let mut game = Game::new(&players, settings).unwrap();
|
||||
game.r#continue().r#continue();
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
|
||||
game.r#continue().sleep();
|
||||
|
||||
game.next_expect_day();
|
||||
game.execute().title().protector();
|
||||
game.mark(game.character_by_player_id(shapeshifter).character_id());
|
||||
game.r#continue().sleep();
|
||||
|
||||
game.next().title().wolf_pack_kill();
|
||||
game.mark(game.character_by_player_id(hunter).character_id());
|
||||
game.r#continue().r#continue();
|
||||
|
||||
game.next().title().shapeshifter();
|
||||
match game
|
||||
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::Shapeshift,
|
||||
)))
|
||||
.unwrap()
|
||||
{
|
||||
ServerToHostMessage::ActionResult(_, res) => assert_eq!(res, ActionResult::ShiftFailed),
|
||||
other => panic!("expected action result, got {other:?}"),
|
||||
}
|
||||
|
||||
game.r#continue().sleep();
|
||||
|
||||
game.next().title().hunter();
|
||||
game.mark_villager();
|
||||
game.r#continue().sleep();
|
||||
|
||||
game.next_expect_day();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use core::num::NonZeroU8;
|
|||
use chrono::{DateTime, Utc};
|
||||
pub use ident::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use werewolves_macros::Titles;
|
||||
|
||||
use crate::{
|
||||
character::CharacterId,
|
||||
|
|
@ -62,7 +63,7 @@ pub struct DayCharacter {
|
|||
pub alive: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Titles)]
|
||||
pub enum ServerToClientMessage {
|
||||
Disconnect,
|
||||
LobbyInfo {
|
||||
|
|
@ -73,9 +74,6 @@ pub enum ServerToClientMessage {
|
|||
GameStart {
|
||||
role: RoleTitle,
|
||||
},
|
||||
InvalidMessageForGameState,
|
||||
NoSuchTarget,
|
||||
GameOver(GameOver),
|
||||
Story(GameStory),
|
||||
Update(PlayerUpdate),
|
||||
DeadChat(Box<[DeadChatMessage]>),
|
||||
|
|
@ -85,7 +83,7 @@ pub enum ServerToClientMessage {
|
|||
Error(GameError),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PlayerUpdate {
|
||||
Number(NonZeroU8),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ pub enum ServerToHostMessage {
|
|||
Lobby {
|
||||
players: Box<[PlayerState]>,
|
||||
settings: GameSettings,
|
||||
qr_mode: bool,
|
||||
},
|
||||
QrMode(bool),
|
||||
Error(GameError),
|
||||
GameOver(GameOver),
|
||||
WaitingForRoleRevealAcks {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ impl PlayerId {
|
|||
pub const fn from_u128(v: u128) -> Self {
|
||||
Self(uuid::Uuid::from_u128(v))
|
||||
}
|
||||
pub const fn from_uuid(uuid: uuid::Uuid) -> Self {
|
||||
Self(uuid)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PlayerId> for uuid::Uuid {
|
||||
fn from(value: PlayerId) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PlayerId {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use werewolves_macros::{All, ChecksAs, RefAndMut, Titles};
|
|||
use crate::{
|
||||
character::CharacterId,
|
||||
diedto::DiedTo,
|
||||
game::{GameTime, Village},
|
||||
game::{Category, GameTime, Village},
|
||||
message::CharacterIdentity,
|
||||
};
|
||||
|
||||
|
|
@ -122,50 +122,59 @@ pub enum Role {
|
|||
#[checks(Alignment::Village)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks(Powerful::NotPowerful)]
|
||||
#[checks(Category::Villager)]
|
||||
Villager,
|
||||
#[checks(Alignment::Wolves)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Category::Villager)]
|
||||
Scapegoat { redeemed: bool },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Seer,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Arcanist,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Adjudicator,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
PowerSeer,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Mortician,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Beholder,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks(Category::Intel)]
|
||||
MasonLeader {
|
||||
recruits_available: u8,
|
||||
recruits: Box<[CharacterId]>,
|
||||
|
|
@ -173,58 +182,69 @@ pub enum Role {
|
|||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks(Category::Intel)]
|
||||
Empath { cursed: bool },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Defensive)]
|
||||
Vindicator,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Defensive)]
|
||||
Diseased,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Defensive)]
|
||||
BlackKnight { attacked: Option<DiedTo> },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Offensive)]
|
||||
Weightlifter,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Offensive)]
|
||||
PyreMaster { villagers_killed: u8 },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Gravedigger,
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks]
|
||||
#[checks(Category::Offensive)]
|
||||
Hunter { target: Option<CharacterId> },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Offensive)]
|
||||
Militia { targeted: Option<CharacterId> },
|
||||
#[checks(Alignment::Wolves)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Offensive)]
|
||||
MapleWolf { last_kill_on_night: u8 },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Defensive)]
|
||||
Guardian {
|
||||
last_protected: Option<PreviousGuardianAction>,
|
||||
},
|
||||
|
|
@ -232,14 +252,17 @@ pub enum Role {
|
|||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("is_mentor")]
|
||||
#[checks(Category::Defensive)]
|
||||
Protector { last_protected: Option<CharacterId> },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks(Category::StartsAsVillager)]
|
||||
Apprentice(RoleTitle),
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks(Category::StartsAsVillager)]
|
||||
Elder {
|
||||
knows_on_night: NonZeroU8,
|
||||
woken_for_reveal: bool,
|
||||
|
|
@ -249,6 +272,7 @@ pub enum Role {
|
|||
#[checks(Powerful::Powerful)]
|
||||
#[checks(Killer::NotKiller)]
|
||||
#[checks("doesnt_wake_if_died_tonight")]
|
||||
#[checks(Category::Intel)]
|
||||
Insomniac,
|
||||
|
||||
#[checks(Alignment::Wolves)]
|
||||
|
|
@ -256,33 +280,39 @@ pub enum Role {
|
|||
#[checks(Powerful::Powerful)]
|
||||
#[checks("wolf")]
|
||||
#[checks("killing_wolf")]
|
||||
#[checks(Category::Wolves)]
|
||||
Werewolf,
|
||||
#[checks(Alignment::Wolves)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("wolf")]
|
||||
#[checks("killing_wolf")]
|
||||
#[checks(Category::Wolves)]
|
||||
AlphaWolf { killed: Option<CharacterId> },
|
||||
#[checks(Alignment::Village)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("wolf")]
|
||||
#[checks(Category::Wolves)]
|
||||
DireWolf { last_blocked: Option<CharacterId> },
|
||||
#[checks(Alignment::Wolves)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("wolf")]
|
||||
#[checks("killing_wolf")]
|
||||
#[checks(Category::Wolves)]
|
||||
Shapeshifter { shifted_into: Option<CharacterId> },
|
||||
#[checks(Alignment::Wolves)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("wolf")]
|
||||
#[checks(Category::Wolves)]
|
||||
LoneWolf,
|
||||
#[checks(Alignment::Wolves)]
|
||||
#[checks(Killer::Killer)]
|
||||
#[checks(Powerful::Powerful)]
|
||||
#[checks("wolf")]
|
||||
#[checks(Category::Wolves)]
|
||||
Bloodletter,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue