From 08db7f9bfc1981ae13668f420cc701dec7527981 Mon Sep 17 00:00:00 2001 From: emilis Date: Thu, 9 Oct 2025 22:27:21 +0100 Subject: [PATCH] pages for prompts, daytime setup --- werewolves-proto/src/game/mod.rs | 14 ++- werewolves-proto/src/game/night.rs | 44 +++++++-- werewolves-proto/src/game/village.rs | 7 ++ werewolves-proto/src/game_test/mod.rs | 52 ++++------- werewolves-proto/src/game_test/previous.rs | 26 ++++-- .../src/game_test/role/shapeshifter.rs | 46 ++++------ werewolves-proto/src/message/host.rs | 4 +- werewolves-server/src/lobby.rs | 30 +++++- werewolves-server/src/runner.rs | 12 ++- werewolves/index.scss | 19 ++++ werewolves/src/clients/host/host.rs | 91 ++++++++++--------- werewolves/src/components/action/picker.rs | 11 ++- werewolves/src/components/action/prompt.rs | 30 ++++++ werewolves/src/pages/role_page.rs | 41 +++++++++ werewolves/src/pages/role_page/beholder.rs | 21 +++++ 15 files changed, 315 insertions(+), 133 deletions(-) create mode 100644 werewolves/src/pages/role_page.rs create mode 100644 werewolves/src/pages/role_page/beholder.rs diff --git a/werewolves-proto/src/game/mod.rs b/werewolves-proto/src/game/mod.rs index 82f915c..859ca79 100644 --- a/werewolves-proto/src/game/mod.rs +++ b/werewolves-proto/src/game/mod.rs @@ -56,6 +56,10 @@ impl Game { pub fn process(&mut self, message: HostGameMessage) -> Result { match (&mut self.state, message) { + (GameState::Night { night }, HostGameMessage::Night(HostNightMessage::NextPage)) => { + night.next_page(); + self.process(HostGameMessage::GetState) + } (GameState::Night { night }, HostGameMessage::Night(HostNightMessage::Next)) => { night.next()?; self.process(HostGameMessage::GetState) @@ -106,6 +110,7 @@ impl Game { DateTime::Day { number } => number, DateTime::Night { number: _ } => unreachable!(), }, + settings: village.settings(), }) } (GameState::Night { night }, HostGameMessage::GetState) => { @@ -115,8 +120,8 @@ impl Game { res.clone(), )); } - if let Some(prompt) = night.current_prompt() { - return Ok(ServerToHostMessage::ActionPrompt(prompt.clone())); + if let Some((prompt, page)) = night.current_prompt() { + return Ok(ServerToHostMessage::ActionPrompt(prompt.clone(), page)); } match night.next() { Ok(_) => self.process(HostGameMessage::GetState), @@ -137,7 +142,10 @@ impl Game { GameState::Night { night }, HostGameMessage::Night(HostNightMessage::ActionResponse(resp)), ) => match night.received_response(resp.clone()) { - Ok(ServerAction::Prompt(prompt)) => Ok(ServerToHostMessage::ActionPrompt(prompt)), + Ok(ServerAction::Prompt(prompt)) => Ok(ServerToHostMessage::ActionPrompt( + prompt, + night.page().unwrap_or_default(), + )), Ok(ServerAction::Result(res)) => Ok(ServerToHostMessage::ActionResult( night.current_character().map(|c| c.identity()), res, diff --git a/werewolves-proto/src/game/night.rs b/werewolves-proto/src/game/night.rs index 6849c9f..9a579d9 100644 --- a/werewolves-proto/src/game/night.rs +++ b/werewolves-proto/src/game/night.rs @@ -236,6 +236,7 @@ enum NightState { current_prompt: ActionPrompt, current_result: Option, current_changes: Vec, + current_page: usize, }, Complete, } @@ -294,6 +295,7 @@ impl Night { current_prompt: ActionPrompt::CoverOfDarkness, current_changes: Vec::new(), current_result: None, + current_page: 0, }; Ok(Self { @@ -341,7 +343,15 @@ impl Night { current_prompt, current_result, current_changes, - } => (current_prompt, current_result, current_changes), + current_page, + } => { + if let Some(page_back) = current_page.checked_sub(1) { + *current_page = page_back; + return Ok(()); + } else { + (current_prompt, current_result, current_changes) + } + } NightState::Complete => return Err(GameError::NightOver), }; if let Some((prompt, _, changes)) = self.used_actions.pop() { @@ -621,6 +631,13 @@ impl Night { Ok(()) } + pub const fn page(&self) -> Option { + match &self.night_state { + NightState::Active { current_page, .. } => Some(*current_page), + NightState::Complete => None, + } + } + pub fn received_response(&mut self, resp: ActionResponse) -> Result { match self.received_response_with_role_blocks(resp)? { BlockResolvedOutcome::PromptUpdate(prompt) => match &mut self.night_state { @@ -643,7 +660,7 @@ impl Night { } => current_result.replace(result.clone()), NightState::Complete => return Err(GameError::NightOver), }; - if let NightChange::Shapeshift { source, into } = &change { + if let NightChange::Shapeshift { source, .. } = &change { // needs to be resolved _now_ so that the target can be woken // for the role change with the wolves self.apply_shapeshift(source)?; @@ -692,7 +709,7 @@ impl Night { ) -> Result { let (current_cover, current_wolfy) = self .current_prompt() - .map(|current_prompt| { + .map(|(current_prompt, _)| { ( *current_prompt == ActionPrompt::CoverOfDarkness, current_prompt.is_wolfy(), @@ -752,7 +769,12 @@ impl Night { match self.received_response_consecutive_wolves_dont_sleep(resp)? { ResponseOutcome::PromptUpdate(update) => Ok(BlockResolvedOutcome::PromptUpdate(update)), ResponseOutcome::ActionComplete(ActionComplete { result, change }) => { - match self.current_prompt().ok_or(GameError::NightOver)?.unless() { + match self + .current_prompt() + .ok_or(GameError::NightOver)? + .0 + .unless() + { Some(Unless::TargetBlocked(unless_blocked)) => { if self.changes_from_actions().into_iter().any(|c| match c { NightChange::RoleBlock { @@ -1285,13 +1307,14 @@ impl Night { } } - pub const fn current_prompt(&self) -> Option<&ActionPrompt> { + pub const fn current_prompt(&self) -> Option<(&ActionPrompt, usize)> { match &self.night_state { NightState::Active { current_prompt, current_result: _, + current_page, .. - } => Some(current_prompt), + } => Some((current_prompt, *current_page)), NightState::Complete => None, } } @@ -1351,6 +1374,7 @@ impl Night { current_prompt, current_result: Some(result), current_changes, + .. } => { self.used_actions.push(( current_prompt.clone(), @@ -1377,6 +1401,7 @@ impl Night { current_prompt: prompt, current_result: None, current_changes: Vec::new(), + current_page: 0, }; } else { self.night_state = NightState::Complete; @@ -1534,6 +1559,13 @@ impl Night { .collect(), ) } + + pub fn next_page(&mut self) { + match &mut self.night_state { + NightState::Active { current_page, .. } => *current_page += 1, + _ => {} + } + } } pub enum ServerAction { diff --git a/werewolves-proto/src/game/village.rs b/werewolves-proto/src/game/village.rs index a1df358..ff564e9 100644 --- a/werewolves-proto/src/game/village.rs +++ b/werewolves-proto/src/game/village.rs @@ -1,4 +1,5 @@ use core::num::NonZeroU8; +use std::{rc::Rc, sync::Arc}; use rand::Rng; use serde::{Deserialize, Serialize}; @@ -18,6 +19,7 @@ use crate::{ pub struct Village { characters: Box<[Character]>, date_time: DateTime, + settings: GameSettings, } impl Village { @@ -33,11 +35,16 @@ impl Village { characters.sort_by_key(|l| l.number()); Ok(Self { + settings, characters, date_time: DateTime::Night { number: 0 }, }) } + pub fn settings(&self) -> GameSettings { + self.settings.clone() + } + pub fn killing_wolf(&self) -> Option<&Character> { let wolves = self.characters.iter().filter(|c| c.is_wolf()); diff --git a/werewolves-proto/src/game_test/mod.rs b/werewolves-proto/src/game_test/mod.rs index 861d8ea..1e64381 100644 --- a/werewolves-proto/src/game_test/mod.rs +++ b/werewolves-proto/src/game_test/mod.rs @@ -209,6 +209,7 @@ impl ServerToHostMessageExt for ServerToHostMessage { characters, marked, day, + .. } => (characters, marked, day), resp => panic!("expected daytime, got {resp:?}"), } @@ -216,12 +217,8 @@ impl ServerToHostMessageExt for ServerToHostMessage { fn prompt(self) -> ActionPrompt { match self { - Self::ActionPrompt(prompt) => prompt, - Self::Daytime { - characters: _, - marked: _, - day: _, - } => panic!("{}", "[got daytime]".bold().red()), + Self::ActionPrompt(prompt, _) => prompt, + Self::Daytime { .. } => panic!("{}", "[got daytime]".bold().red()), msg => panic!("expected server message <<{msg:?}>> to be an ActionPrompt"), } } @@ -447,6 +444,7 @@ impl GameExt for Game { characters, marked, day, + .. } => (characters, marked, day), res => panic!("unexpected response to next_expect_day: {res:?}"), } @@ -474,6 +472,7 @@ impl GameExt for Game { characters, marked, day, + .. } => (characters, marked, day), res => panic!("unexpected response to mark_for_execution: {res:?}"), } @@ -547,7 +546,7 @@ fn starts_with_wolf_intro() { let resp = game.process(HostGameMessage::GetState).unwrap(); assert_eq!( resp, - ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness) + ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness, 0) ) } @@ -578,7 +577,7 @@ fn no_wolf_kill_n1() { assert!(matches!( game.process(HostGameMessage::Night(HostNightMessage::Next)) .unwrap(), - ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }) + ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }, 0) )); assert_eq!( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( @@ -587,15 +586,7 @@ fn no_wolf_kill_n1() { .unwrap(), ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep), ); - assert!(matches!( - game.process(HostGameMessage::Night(HostNightMessage::Next)) - .unwrap(), - ServerToHostMessage::Daytime { - characters: _, - marked: _, - day: _, - } - )); + game.next_expect_day(); } #[test] @@ -616,7 +607,7 @@ fn yes_wolf_kill_n2() { assert!(matches!( game.process(HostGameMessage::Night(HostNightMessage::Next)) .unwrap(), - ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }) + ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }, 0) )); assert_eq!( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( @@ -626,15 +617,7 @@ fn yes_wolf_kill_n2() { .result(), ActionResult::GoBackToSleep, ); - assert!(matches!( - game.process(HostGameMessage::Night(HostNightMessage::Next)) - .unwrap(), - ServerToHostMessage::Daytime { - characters: _, - marked: _, - day: _, - } - )); + game.next_expect_day(); let execution_target = game .village() @@ -653,6 +636,7 @@ fn yes_wolf_kill_n2() { characters: _, marked, day: _, + .. } => assert_eq!(marked.to_vec(), vec![execution_target]), resp => panic!("unexpected server message: {resp:#?}"), } @@ -660,7 +644,7 @@ fn yes_wolf_kill_n2() { assert_eq!( game.process(HostGameMessage::Day(HostDayMessage::Execute)) .unwrap(), - ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness) + ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness, 0) ); assert_eq!( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( @@ -673,10 +657,13 @@ fn yes_wolf_kill_n2() { assert!(matches!( game.process(HostGameMessage::Night(HostNightMessage::Next)) .unwrap(), - ServerToHostMessage::ActionPrompt(ActionPrompt::WolfPackKill { - living_villagers: _, - marked: _, - }) + ServerToHostMessage::ActionPrompt( + ActionPrompt::WolfPackKill { + living_villagers: _, + marked: _, + }, + 0 + ) )); } @@ -720,6 +707,7 @@ fn wolfpack_kill_all_targets_valid() { characters: _, marked, day: _, + .. } => assert_eq!(marked.to_vec(), vec![execution_target]), resp => panic!("unexpected server message: {resp:#?}"), } diff --git a/werewolves-proto/src/game_test/previous.rs b/werewolves-proto/src/game_test/previous.rs index beba9ce..dda96c1 100644 --- a/werewolves-proto/src/game_test/previous.rs +++ b/werewolves-proto/src/game_test/previous.rs @@ -48,11 +48,14 @@ fn previous_shapeshifter_undone_redone() { } assert_eq!( game.get_state(), - ServerToHostMessage::ActionPrompt(ActionPrompt::Shapeshifter { - character_id: game - .character_by_player_id(shapeshifter_player_id) - .identity() - }) + ServerToHostMessage::ActionPrompt( + ActionPrompt::Shapeshifter { + character_id: game + .character_by_player_id(shapeshifter_player_id) + .identity() + }, + 0 + ) ); game.response(ActionResponse::Shapeshift).r#continue(); assert_eq!( @@ -104,11 +107,14 @@ fn previous_shapeshifter_undone_and_changed_to_no() { } assert_eq!( game.get_state(), - ServerToHostMessage::ActionPrompt(ActionPrompt::Shapeshifter { - character_id: game - .character_by_player_id(shapeshifter_player_id) - .identity() - }) + ServerToHostMessage::ActionPrompt( + ActionPrompt::Shapeshifter { + character_id: game + .character_by_player_id(shapeshifter_player_id) + .identity() + }, + 0 + ) ); game.r#continue().sleep(); diff --git a/werewolves-proto/src/game_test/role/shapeshifter.rs b/werewolves-proto/src/game_test/role/shapeshifter.rs index cfab08f..2163eae 100644 --- a/werewolves-proto/src/game_test/role/shapeshifter.rs +++ b/werewolves-proto/src/game_test/role/shapeshifter.rs @@ -40,11 +40,7 @@ fn protect_stops_shapeshift() { .unwrap(), ServerToHostMessage::ActionResult(None, ActionResult::Continue) ); - assert!(matches!( - game.process(HostGameMessage::Night(HostNightMessage::Next)) - .unwrap(), - ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }) - )); + game.next().title().wolves_intro(); assert_eq!( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( ActionResponse::Continue @@ -52,15 +48,7 @@ fn protect_stops_shapeshift() { .unwrap(), ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep), ); - assert!(matches!( - game.process(HostGameMessage::Night(HostNightMessage::Next)) - .unwrap(), - ServerToHostMessage::Daytime { - characters: _, - marked: _, - day: _, - } - )); + game.next_expect_day(); let execution_target = game .village() @@ -75,11 +63,9 @@ fn protect_stops_shapeshift() { ))) .unwrap() { - ServerToHostMessage::Daytime { - characters: _, - marked, - day: _, - } => assert_eq!(marked.to_vec(), vec![execution_target]), + ServerToHostMessage::Daytime { marked, .. } => { + assert_eq!(marked.to_vec(), vec![execution_target]) + } resp => panic!("unexpected server message: {resp:#?}"), } @@ -96,11 +82,14 @@ fn protect_stops_shapeshift() { .process(HostGameMessage::Night(HostNightMessage::Next)) .unwrap() { - ServerToHostMessage::ActionPrompt(ActionPrompt::Protector { - character_id: prot_char_id, - targets, - marked: None, - }) => ( + ServerToHostMessage::ActionPrompt( + ActionPrompt::Protector { + character_id: prot_char_id, + targets, + marked: None, + }, + 0, + ) => ( targets .into_iter() .map(|c| game.village().character_by_id(c.character_id).unwrap()) @@ -124,9 +113,12 @@ fn protect_stops_shapeshift() { ))) .unwrap() { - ServerToHostMessage::ActionPrompt(ActionPrompt::Protector { - marked: Some(mark), .. - }) => assert_eq!(mark, prot_and_wolf_target, "marked target"), + ServerToHostMessage::ActionPrompt( + ActionPrompt::Protector { + marked: Some(mark), .. + }, + 0, + ) => assert_eq!(mark, prot_and_wolf_target, "marked target"), resp => panic!("unexpected response: {resp:?}"), } diff --git a/werewolves-proto/src/message/host.rs b/werewolves-proto/src/message/host.rs index 8b593fe..481dd28 100644 --- a/werewolves-proto/src/message/host.rs +++ b/werewolves-proto/src/message/host.rs @@ -37,6 +37,7 @@ pub enum HostGameMessage { pub enum HostNightMessage { ActionResponse(ActionResponse), Next, + NextPage, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -69,8 +70,9 @@ pub enum ServerToHostMessage { characters: Box<[CharacterState]>, marked: Box<[CharacterId]>, day: NonZeroU8, + settings: GameSettings, }, - ActionPrompt(ActionPrompt), + ActionPrompt(ActionPrompt, usize), ActionResult(Option, ActionResult), Lobby(Box<[PlayerState]>), QrMode(bool), diff --git a/werewolves-server/src/lobby.rs b/werewolves-server/src/lobby.rs index 5690ae8..83391bb 100644 --- a/werewolves-server/src/lobby.rs +++ b/werewolves-server/src/lobby.rs @@ -1,11 +1,14 @@ -use core::ops::{Deref, DerefMut}; +use core::{ + num::NonZeroU8, + ops::{Deref, DerefMut}, +}; -use tokio::sync::broadcast::Sender; +use tokio::sync::broadcast::{self, Sender}; use werewolves_proto::{ error::GameError, game::{Game, GameSettings}, message::{ - ClientMessage, Identification, PlayerState, ServerMessage, + ClientMessage, Identification, PlayerState, PublicIdentity, ServerMessage, host::{HostLobbyMessage, HostMessage, ServerToHostMessage}, }, player::PlayerId, @@ -335,6 +338,27 @@ impl DerefMut for LobbyPlayers { } impl LobbyPlayers { + pub fn with_dummies(dummy_count: NonZeroU8) -> Self { + let (send, mut recv) = broadcast::channel(100); + tokio::spawn(async move { while recv.recv().await.is_ok() {} }); + Self( + (0..dummy_count.get()) + .map(|p| { + ( + Identification { + player_id: PlayerId::from_u128(p as u128 + 1), + public: PublicIdentity { + name: format!("Player {}", p as u16 + 1), + pronouns: Some(String::from("he/him")), + number: NonZeroU8::new(p + 1), + }, + }, + send.clone(), + ) + }) + .collect(), + ) + } pub fn find(&self, player_id: PlayerId) -> Option<&Sender> { self.iter() .find_map(|(id, s)| (id.player_id == player_id).then_some(s)) diff --git a/werewolves-server/src/runner.rs b/werewolves-server/src/runner.rs index 788a8de..f9460ec 100644 --- a/werewolves-server/src/runner.rs +++ b/werewolves-server/src/runner.rs @@ -1,4 +1,4 @@ -use core::time::Duration; +use core::{num::NonZeroU8, time::Duration}; use std::sync::Arc; use werewolves_proto::{ @@ -11,7 +11,7 @@ use crate::{ communication::lobby::LobbyComms, connection::JoinedPlayers, game::{GameEnd, GameRunner}, - lobby::Lobby, + lobby::{Lobby, LobbyPlayers}, saver::Saver, }; @@ -22,7 +22,13 @@ pub struct IdentifiedClientMessage { } pub async fn run_game(joined_players: JoinedPlayers, comms: LobbyComms, mut saver: impl Saver) { - let mut state = RunningState::Lobby(Lobby::new(joined_players, comms)); + let mut lobby = Lobby::new(joined_players, comms); + if let Some(dummies) = option_env!("DUMMY_PLAYERS").and_then(|p| p.parse::().ok()) { + log::info!("creating {dummies} dummy players"); + lobby.set_players_in_lobby(LobbyPlayers::with_dummies(dummies)); + lobby.send_lobby_info_to_host().await.log_debug(); + } + let mut state = RunningState::Lobby(lobby); loop { match &mut state { RunningState::Lobby(lobby) => { diff --git a/werewolves/index.scss b/werewolves/index.scss index 3116395..d40ae40 100644 --- a/werewolves/index.scss +++ b/werewolves/index.scss @@ -931,6 +931,13 @@ input { $village_bg: color.change($village_color, $alpha: 0.3); $village_border: color.change($village_color, $alpha: 1.0); + .targets { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-evenly; + } + .character { padding: 0.5cm; @@ -1305,3 +1312,15 @@ input { flex-direction: column; flex-wrap: nowrap; } + +.page { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + align-items: center; + width: 100%; + height: 100%; + // width: ; + + & .next {} +} diff --git a/werewolves/src/clients/host/host.rs b/werewolves/src/clients/host/host.rs index 866f373..1d7ac15 100644 --- a/werewolves/src/clients/host/host.rs +++ b/werewolves/src/clients/host/host.rs @@ -31,6 +31,7 @@ use crate::{ action::{ActionResultView, Prompt}, host::{DaytimePlayerList, Setup}, }, + pages::RolePage, storage::StorageKey, }; @@ -196,6 +197,7 @@ pub enum HostState { characters: Box<[CharacterState]>, marked_for_execution: Box<[CharacterId]>, day: NonZeroU8, + settings: GameSettings, }, GameOver { result: GameOver, @@ -204,7 +206,7 @@ pub enum HostState { ackd: Box<[CharacterIdentity]>, waiting: Box<[CharacterIdentity]>, }, - Prompt(ActionPrompt), + Prompt(ActionPrompt, usize), Result(Option, ActionResult), } @@ -217,10 +219,12 @@ impl From for HostEvent { characters, day, marked: marked_for_execution, + settings, } => HostEvent::SetState(HostState::Day { characters, day, marked_for_execution, + settings, }), ServerToHostMessage::Lobby(players) => HostEvent::PlayerList(players), ServerToHostMessage::GameSettings(settings) => HostEvent::Settings(settings), @@ -228,8 +232,8 @@ impl From for HostEvent { ServerToHostMessage::GameOver(game_over) => { HostEvent::SetState(HostState::GameOver { result: game_over }) } - ServerToHostMessage::ActionPrompt(prompt) => { - HostEvent::SetState(HostState::Prompt(prompt)) + ServerToHostMessage::ActionPrompt(prompt, page) => { + HostEvent::SetState(HostState::Prompt(prompt, page)) } ServerToHostMessage::ActionResult(_, ActionResult::Continue) => HostEvent::Continue, ServerToHostMessage::ActionResult(ident, result) => { @@ -309,7 +313,7 @@ impl Component for Host { }, HostState::Lobby { players, settings } => { if self.big_screen { - self.lobby_big_screen_show_setup(players, settings) + self.lobby_big_screen_show_setup(settings) } else { self.lobby_setup(players, settings) } @@ -318,30 +322,35 @@ impl Component for Host { characters, day, marked_for_execution, + settings, } => { - let on_mark = crate::callback::send_fn( - |target| { - HostMessage::InGame(HostGameMessage::Day(HostDayMessage::MarkForExecution( - target, - ))) - }, - self.send.clone(), - ); - let on_execute = crate::callback::send_message( - HostMessage::InGame(HostGameMessage::Day(HostDayMessage::Execute)), - self.send.clone(), - ); - html! { - >() - } - on_execute={on_execute} - on_mark={on_mark} - /> + if self.big_screen { + self.lobby_big_screen_show_setup(settings) + } else { + let on_mark = crate::callback::send_fn( + |target| { + HostMessage::InGame(HostGameMessage::Day( + HostDayMessage::MarkForExecution(target), + )) + }, + self.send.clone(), + ); + let on_execute = crate::callback::send_message( + HostMessage::InGame(HostGameMessage::Day(HostDayMessage::Execute)), + self.send.clone(), + ); + html! { + >() + } + on_execute={on_execute} + on_mark={on_mark} + /> + } } } HostState::RoleReveal { ackd, waiting } => { @@ -364,7 +373,7 @@ impl Component for Host { } } - HostState::Prompt(prompt) => { + HostState::Prompt(prompt, page) => { let send = self.send.clone(); let on_complete = Callback::from(move |msg| { let mut send = send.clone(); @@ -374,9 +383,12 @@ impl Component for Host { } }); }); + let pages = prompt.role_pages(self.big_screen); html! { { + | HostState::RoleReveal { .. } + | HostState::Day { .. } => { return false; } } @@ -526,7 +531,7 @@ impl Component for Host { *s = settings; true } - HostState::Prompt(_) + HostState::Prompt(_, _) | HostState::Result(_, _) | HostState::Disconnected | HostState::RoleReveal { @@ -534,11 +539,7 @@ impl Component for Host { waiting: _, } | HostState::GameOver { result: _ } - | HostState::Day { - characters: _, - day: _, - marked_for_execution: _, - } => { + | HostState::Day { .. } => { log::info!("ignoring settings get"); false } @@ -588,7 +589,7 @@ impl Component for Host { } impl Host { - fn lobby_big_screen_show_setup(&self, _: Rc<[PlayerState]>, settings: GameSettings) -> Html { + fn lobby_big_screen_show_setup(&self, settings: GameSettings) -> Html { if !self.qr_mode { return html! { diff --git a/werewolves/src/components/action/picker.rs b/werewolves/src/components/action/picker.rs index 7b66ab8..726fc5d 100644 --- a/werewolves/src/components/action/picker.rs +++ b/werewolves/src/components/action/picker.rs @@ -33,15 +33,20 @@ pub fn TargetPicker( } }) .collect::(); + let continue_button = continue_callback.clone().map(|continue_callback| { + html! { + + } + }); html! {
{targets}
- + {continue_button}
} } diff --git a/werewolves/src/components/action/prompt.rs b/werewolves/src/components/action/prompt.rs index 7a99ca7..c70e26a 100644 --- a/werewolves/src/components/action/prompt.rs +++ b/werewolves/src/components/action/prompt.rs @@ -1,4 +1,5 @@ use core::ops::Not; +use std::rc::Rc; use werewolves_proto::{ character::CharacterId, @@ -22,6 +23,10 @@ pub struct ActionPromptProps { #[prop_or_default] pub big_screen: bool, pub on_complete: Callback, + #[prop_or_default] + pub page_idx: usize, + #[prop_or_default] + pub pages: Rc<[Html]>, } fn identity_html(props: &ActionPromptProps, ident: Option<&CharacterIdentity>) -> Option { @@ -41,6 +46,31 @@ fn identity_html(props: &ActionPromptProps, ident: Option<&CharacterIdentity>) - #[function_component] pub fn Prompt(props: &ActionPromptProps) -> Html { + if let Some(page) = props.pages.get(props.page_idx).map(|page| { + let next = props.big_screen.not().then(|| { + let send = props.on_complete.clone(); + let on_page_next = Callback::from(move |_| { + send.emit(HostMessage::InGame(HostGameMessage::Night( + HostNightMessage::NextPage, + ))) + }); + html! { + + } + }); + + html! { +
+ {page.clone()} + {next} +
+ } + }) { + return page; + } + let on_complete = props.on_complete.clone(); let continue_callback = props.big_screen.not().then(|| { Callback::from(move |_| { diff --git a/werewolves/src/pages/role_page.rs b/werewolves/src/pages/role_page.rs new file mode 100644 index 0000000..df2ba2b --- /dev/null +++ b/werewolves/src/pages/role_page.rs @@ -0,0 +1,41 @@ +use core::ops::Not; +use std::rc::Rc; + +use werewolves_proto::message::{PublicIdentity, night::ActionPrompt}; +use yew::prelude::*; + +use crate::components::Identity; + +werewolves_macros::include_path!("werewolves/src/pages/role_page"); +pub trait RolePage { + fn role_pages(&self, big_screen: bool) -> Rc<[yew::Html]>; +} + +impl RolePage for ActionPrompt { + fn role_pages(&self, big_screen: bool) -> Rc<[yew::Html]> { + match self { + ActionPrompt::Beholder { character_id, .. } => { + let ident = big_screen.not().then(|| { + html! { + ::into(character_id)} /> + } + }); + Rc::new([ + html! { + <> + {ident.clone()} + + + }, + html! { + <> + {ident} + + + }, + ]) + } + _ => Rc::new([]), + } + } +} diff --git a/werewolves/src/pages/role_page/beholder.rs b/werewolves/src/pages/role_page/beholder.rs new file mode 100644 index 0000000..5032d48 --- /dev/null +++ b/werewolves/src/pages/role_page/beholder.rs @@ -0,0 +1,21 @@ +use yew::prelude::*; + +#[function_component] +pub fn BeholderPage1() -> Html { + html! { +
+

{"this is a beholder"}

+

{"there's information on this page"}

+
+ } +} + +#[function_component] +pub fn BeholderPage2() -> Html { + html! { +
+

{"more information on beholder"}

+

{"idk, hi?"}

+
+ } +}