diff --git a/werewolves-proto/src/character.rs b/werewolves-proto/src/character.rs index b609024..5221c39 100644 --- a/werewolves-proto/src/character.rs +++ b/werewolves-proto/src/character.rs @@ -1,4 +1,8 @@ -use core::{fmt::Display, num::NonZeroU8, ops::Not}; +use core::{ + fmt::Display, + num::NonZeroU8, + ops::{Deref, Not}, +}; use rand::seq::SliceRandom; use serde::{Deserialize, Serialize}; @@ -272,6 +276,14 @@ impl Character { .collect()) } + /// Returns a copy of this character with their role replaced + /// in a read-only type + pub fn as_role(&self, role: Role) -> AsCharacter { + let mut char = self.clone(); + char.role = role; + AsCharacter(char) + } + pub fn night_action_prompts(&self, village: &Village) -> Result> { if self.mason_leader().is_ok() { return self.mason_prompts(village); @@ -834,3 +846,13 @@ impl MasonLeaderMut<'_> { } } } + +pub struct AsCharacter(Character); + +impl Deref for AsCharacter { + type Target = Character; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/werewolves-proto/src/game/night.rs b/werewolves-proto/src/game/night.rs index f1f8f3d..37fe8ce 100644 --- a/werewolves-proto/src/game/night.rs +++ b/werewolves-proto/src/game/night.rs @@ -270,7 +270,31 @@ impl Night { .partial_cmp(right_prompt) .unwrap_or(core::cmp::Ordering::Equal) }); - let mut action_queue = VecDeque::from(action_queue); + + let mut action_queue = VecDeque::from({ + // insert actions for role-changed roles + let mut expanded_queue = Vec::new(); + for action in action_queue { + match &action { + ActionPrompt::RoleChange { + character_id, + new_role, + } => { + let char = village.character_by_id(character_id.character_id)?; + let as_role = char.as_role(new_role.title_to_role_excl_apprentice()); + expanded_queue.push(action); + for prompt in as_role.night_action_prompts(&village)? { + expanded_queue.push(prompt); + } + } + _ => { + expanded_queue.push(action); + continue; + } + } + } + expanded_queue + }); if night == 0 { action_queue.push_front(ActionPrompt::WolvesIntro { @@ -716,6 +740,39 @@ impl Night { } } + fn received_response_consecutive_same_player_no_sleep( + &self, + resp: ActionResponse, + ) -> Result { + let same_char = self + .current_character_id() + .and_then(|curr| { + self.action_queue + .iter() + .next() + .map(|n| n.character_id() == Some(curr)) + }) + .unwrap_or_default(); + + match ( + self.received_response_consecutive_wolves_dont_sleep(resp)?, + same_char, + ) { + (ResponseOutcome::PromptUpdate(p), _) => Ok(ResponseOutcome::PromptUpdate(p)), + ( + ResponseOutcome::ActionComplete(ActionComplete { + result: ActionResult::GoBackToSleep, + change, + }), + true, + ) => Ok(ResponseOutcome::ActionComplete(ActionComplete { + result: ActionResult::Continue, + change, + })), + (act, _) => Ok(act), + } + } + fn received_response_consecutive_wolves_dont_sleep( &self, resp: ActionResponse, @@ -775,7 +832,7 @@ impl Night { &self, resp: ActionResponse, ) -> Result { - match self.received_response_consecutive_wolves_dont_sleep(resp)? { + match self.received_response_consecutive_same_player_no_sleep(resp)? { ResponseOutcome::PromptUpdate(update) => Ok(BlockResolvedOutcome::PromptUpdate(update)), ResponseOutcome::ActionComplete(ActionComplete { result, change }) => { match self @@ -864,36 +921,7 @@ impl Night { current_prompt, current_result: _, .. - } => match current_prompt { - ActionPrompt::Insomniac { character_id, .. } - | ActionPrompt::LoneWolfKill { character_id, .. } - | ActionPrompt::ElderReveal { character_id } - | ActionPrompt::RoleChange { character_id, .. } - | ActionPrompt::Seer { character_id, .. } - | ActionPrompt::Protector { character_id, .. } - | ActionPrompt::Arcanist { character_id, .. } - | ActionPrompt::Gravedigger { character_id, .. } - | ActionPrompt::Hunter { character_id, .. } - | ActionPrompt::Militia { character_id, .. } - | ActionPrompt::MapleWolf { character_id, .. } - | ActionPrompt::Guardian { character_id, .. } - | ActionPrompt::Shapeshifter { character_id } - | ActionPrompt::AlphaWolf { character_id, .. } - | ActionPrompt::Adjudicator { character_id, .. } - | ActionPrompt::PowerSeer { character_id, .. } - | ActionPrompt::Mortician { character_id, .. } - | ActionPrompt::Beholder { character_id, .. } - | ActionPrompt::MasonLeaderRecruit { character_id, .. } - | ActionPrompt::Empath { character_id, .. } - | ActionPrompt::Vindicator { character_id, .. } - | ActionPrompt::PyreMaster { character_id, .. } - | ActionPrompt::DireWolf { character_id, .. } => Some(character_id.character_id), - - ActionPrompt::WolvesIntro { wolves: _ } - | ActionPrompt::MasonsWake { .. } - | ActionPrompt::WolfPackKill { .. } - | ActionPrompt::CoverOfDarkness => None, - }, + } => current_prompt.character_id(), NightState::Complete => None, } } diff --git a/werewolves-proto/src/message/night.rs b/werewolves-proto/src/message/night.rs index 1fefd79..2f5cbcd 100644 --- a/werewolves-proto/src/message/night.rs +++ b/werewolves-proto/src/message/night.rs @@ -13,36 +13,32 @@ use crate::{ type Result = core::result::Result; -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, ChecksAs)] pub enum ActionType { Cover, + #[checks("is_wolfy")] WolvesIntro, + RoleChange, Protect, + #[checks("is_wolfy")] WolfPackKill, - Direwolf, + #[checks("is_wolfy")] + Shapeshifter, + #[checks("is_wolfy")] + AlphaWolfKill, + #[checks("is_wolfy")] OtherWolf, + #[checks("is_wolfy")] + Direwolf, LoneWolfKill, Block, + VillageKill, Intel, Other, MasonRecruit, MasonsWake, Insomniac, Beholder, - RoleChange, -} - -impl ActionType { - const fn is_wolfy(&self) -> bool { - // note: Lone Wolf isn't wolfy, as they don't wake with wolves - matches!( - self, - ActionType::Direwolf - | ActionType::OtherWolf - | ActionType::WolfPackKill - | ActionType::WolvesIntro - ) - } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ChecksAs, Titles)] @@ -91,7 +87,7 @@ pub enum ActionPrompt { living_players: Box<[CharacterIdentity]>, marked: Option, }, - #[checks(ActionType::Other)] + #[checks(ActionType::VillageKill)] Militia { character_id: CharacterIdentity, living_players: Box<[CharacterIdentity]>, @@ -159,7 +155,7 @@ pub enum ActionPrompt { living_players: Box<[CharacterIdentity]>, marked: Option, }, - #[checks(ActionType::Other)] + #[checks(ActionType::VillageKill)] PyreMaster { character_id: CharacterIdentity, living_players: Box<[CharacterIdentity]>, @@ -171,9 +167,9 @@ pub enum ActionPrompt { living_villagers: Box<[CharacterIdentity]>, marked: Option, }, - #[checks(ActionType::OtherWolf)] + #[checks(ActionType::Shapeshifter)] Shapeshifter { character_id: CharacterIdentity }, - #[checks(ActionType::OtherWolf)] + #[checks(ActionType::AlphaWolfKill)] AlphaWolf { character_id: CharacterIdentity, living_villagers: Box<[CharacterIdentity]>, @@ -196,6 +192,38 @@ pub enum ActionPrompt { } impl ActionPrompt { + pub(crate) const fn character_id(&self) -> Option { + match self { + ActionPrompt::Insomniac { character_id, .. } + | ActionPrompt::LoneWolfKill { character_id, .. } + | ActionPrompt::ElderReveal { character_id } + | ActionPrompt::RoleChange { character_id, .. } + | ActionPrompt::Seer { character_id, .. } + | ActionPrompt::Protector { character_id, .. } + | ActionPrompt::Arcanist { character_id, .. } + | ActionPrompt::Gravedigger { character_id, .. } + | ActionPrompt::Hunter { character_id, .. } + | ActionPrompt::Militia { character_id, .. } + | ActionPrompt::MapleWolf { character_id, .. } + | ActionPrompt::Guardian { character_id, .. } + | ActionPrompt::Shapeshifter { character_id } + | ActionPrompt::AlphaWolf { character_id, .. } + | ActionPrompt::Adjudicator { character_id, .. } + | ActionPrompt::PowerSeer { character_id, .. } + | ActionPrompt::Mortician { character_id, .. } + | ActionPrompt::Beholder { character_id, .. } + | ActionPrompt::MasonLeaderRecruit { character_id, .. } + | ActionPrompt::Empath { character_id, .. } + | ActionPrompt::Vindicator { character_id, .. } + | ActionPrompt::PyreMaster { character_id, .. } + | ActionPrompt::DireWolf { character_id, .. } => Some(character_id.character_id), + + ActionPrompt::WolvesIntro { .. } + | ActionPrompt::MasonsWake { .. } + | ActionPrompt::WolfPackKill { .. } + | ActionPrompt::CoverOfDarkness => None, + } + } pub(crate) fn matches_beholding(&self, target: CharacterId) -> bool { match self { ActionPrompt::Insomniac { character_id, .. } diff --git a/werewolves-proto/src/role.rs b/werewolves-proto/src/role.rs index c782286..f7920bb 100644 --- a/werewolves-proto/src/role.rs +++ b/werewolves-proto/src/role.rs @@ -219,7 +219,6 @@ pub enum Role { #[checks(Alignment::Village)] #[checks(Powerful::Powerful)] #[checks(Killer::NotKiller)] - #[checks("is_mentor")] Elder { knows_on_night: NonZeroU8, woken_for_reveal: bool, diff --git a/werewolves/img/equal.svg b/werewolves/img/equal.svg new file mode 100644 index 0000000..97909e7 --- /dev/null +++ b/werewolves/img/equal.svg @@ -0,0 +1,28 @@ + + + + diff --git a/werewolves/img/not-equal.svg b/werewolves/img/not-equal.svg new file mode 100644 index 0000000..b91b4e8 --- /dev/null +++ b/werewolves/img/not-equal.svg @@ -0,0 +1,35 @@ + + + + diff --git a/werewolves/img/red-x.svg b/werewolves/img/red-x.svg new file mode 100644 index 0000000..3fe0d07 --- /dev/null +++ b/werewolves/img/red-x.svg @@ -0,0 +1,19 @@ + + + + diff --git a/werewolves/index.scss b/werewolves/index.scss index 0477eef..7d6c9a7 100644 --- a/werewolves/index.scss +++ b/werewolves/index.scss @@ -102,7 +102,7 @@ app { left: 0; top: 0; margin: 0; - font-size: 2rem; + font-size: 2.7vw; } $link_color: #432054; @@ -383,16 +383,38 @@ button { } .identity { - font-size: 1.5em; + font-size: 1.5rem; } } +.day-char { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + // min-width: 1vw; + align-items: center; +} + +.red { + color: red; +} + .character { text-align: center; border: 3px solid rgba(0, 0, 0, 0.4); // min-width: 20%; flex-shrink: 1; + .headline { + display: flex; + flex-direction: column; + align-items: center; + flex-wrap: nowrap; + overflow: hidden; + gap: 5px; + + } + .role { font-size: 1.5rem; @@ -881,6 +903,7 @@ error { .binary { margin: 0; padding: 0; + font-size: 1.5vw; .button-container { text-align: center; @@ -1281,7 +1304,7 @@ input { .setup-screen { margin-top: 2%; - font-size: 1rem; + font-size: 1.5vw; .setup { display: flex; @@ -1341,6 +1364,7 @@ input { filter: saturate(40%); padding-left: 10px; padding-right: 10px; + font-weight: bolder; &.wakes { border: 2px solid yellow; @@ -1361,7 +1385,8 @@ input { } .inactive { - filter: grayscale(100%) brightness(30%); + // filter: grayscale(100%) brightness(30%); + filter: brightness(0%); } .qrcode { @@ -1383,6 +1408,7 @@ input { } .details { + font-size: 5vw; // height: 100%; // width: 100%; border: 1px solid $village_border; @@ -1485,12 +1511,17 @@ input { font-size: 1.5rem; } + &.full-height { + height: 100vh; + } + & input { height: 2rem; text-align: center; - &#number { + #number { font-size: 2rem; + max-width: 50vw; } } } @@ -1724,10 +1755,11 @@ input { h1 { text-align: center; // align-self: flex-start; + font-size: 4vw; } .information { - font-size: 1.2em; + font-size: 1.2rem; padding-left: 5%; padding-right: 5%; } @@ -1764,7 +1796,7 @@ input { gap: 10px; &.masons { - font-size: 2em; + font-size: 2rem; } } diff --git a/werewolves/src/clients/host/host.rs b/werewolves/src/clients/host/host.rs index 2197b0d..5c65321 100644 --- a/werewolves/src/clients/host/host.rs +++ b/werewolves/src/clients/host/host.rs @@ -459,13 +459,17 @@ impl Component for Host { } } else { - let to_big = Callback::from(|_| { - if let Some(loc) = gloo::utils::document().location() { - let _ = loc.replace("/host/big"); - } - }); + // let to_big = Callback::from(|_| { + // if let Some(loc) = gloo::utils::document().location() { + // let _ = loc.replace("/host/big"); + // } + // }); html! { - + + + } }; let story_on_click = if let HostState::Story { .. } = &self.state { diff --git a/werewolves/src/components/client/signin.rs b/werewolves/src/components/client/signin.rs index 41e13fe..f0abf49 100644 --- a/werewolves/src/components/client/signin.rs +++ b/werewolves/src/components/client/signin.rs @@ -47,7 +47,7 @@ pub fn Signin(props: &SigninProps) -> Html { let on_change = crate::components::input_element_number_oninput(num_value); html! { -