Compare commits

..

No commits in common. "08db7f9bfc1981ae13668f420cc701dec7527981" and "563a3c5c40865aec06965ba3a739544ab70bce1a" have entirely different histories.

16 changed files with 145 additions and 347 deletions

View File

@ -56,10 +56,6 @@ impl Game {
pub fn process(&mut self, message: HostGameMessage) -> Result<ServerToHostMessage> { pub fn process(&mut self, message: HostGameMessage) -> Result<ServerToHostMessage> {
match (&mut self.state, message) { 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)) => { (GameState::Night { night }, HostGameMessage::Night(HostNightMessage::Next)) => {
night.next()?; night.next()?;
self.process(HostGameMessage::GetState) self.process(HostGameMessage::GetState)
@ -110,7 +106,6 @@ impl Game {
DateTime::Day { number } => number, DateTime::Day { number } => number,
DateTime::Night { number: _ } => unreachable!(), DateTime::Night { number: _ } => unreachable!(),
}, },
settings: village.settings(),
}) })
} }
(GameState::Night { night }, HostGameMessage::GetState) => { (GameState::Night { night }, HostGameMessage::GetState) => {
@ -120,8 +115,8 @@ impl Game {
res.clone(), res.clone(),
)); ));
} }
if let Some((prompt, page)) = night.current_prompt() { if let Some(prompt) = night.current_prompt() {
return Ok(ServerToHostMessage::ActionPrompt(prompt.clone(), page)); return Ok(ServerToHostMessage::ActionPrompt(prompt.clone()));
} }
match night.next() { match night.next() {
Ok(_) => self.process(HostGameMessage::GetState), Ok(_) => self.process(HostGameMessage::GetState),
@ -142,10 +137,7 @@ impl Game {
GameState::Night { night }, GameState::Night { night },
HostGameMessage::Night(HostNightMessage::ActionResponse(resp)), HostGameMessage::Night(HostNightMessage::ActionResponse(resp)),
) => match night.received_response(resp.clone()) { ) => match night.received_response(resp.clone()) {
Ok(ServerAction::Prompt(prompt)) => Ok(ServerToHostMessage::ActionPrompt( Ok(ServerAction::Prompt(prompt)) => Ok(ServerToHostMessage::ActionPrompt(prompt)),
prompt,
night.page().unwrap_or_default(),
)),
Ok(ServerAction::Result(res)) => Ok(ServerToHostMessage::ActionResult( Ok(ServerAction::Result(res)) => Ok(ServerToHostMessage::ActionResult(
night.current_character().map(|c| c.identity()), night.current_character().map(|c| c.identity()),
res, res,

View File

@ -236,7 +236,6 @@ enum NightState {
current_prompt: ActionPrompt, current_prompt: ActionPrompt,
current_result: Option<ActionResult>, current_result: Option<ActionResult>,
current_changes: Vec<NightChange>, current_changes: Vec<NightChange>,
current_page: usize,
}, },
Complete, Complete,
} }
@ -295,7 +294,6 @@ impl Night {
current_prompt: ActionPrompt::CoverOfDarkness, current_prompt: ActionPrompt::CoverOfDarkness,
current_changes: Vec::new(), current_changes: Vec::new(),
current_result: None, current_result: None,
current_page: 0,
}; };
Ok(Self { Ok(Self {
@ -343,15 +341,7 @@ impl Night {
current_prompt, current_prompt,
current_result, current_result,
current_changes, current_changes,
current_page, } => (current_prompt, current_result, current_changes),
} => {
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), NightState::Complete => return Err(GameError::NightOver),
}; };
if let Some((prompt, _, changes)) = self.used_actions.pop() { if let Some((prompt, _, changes)) = self.used_actions.pop() {
@ -631,13 +621,6 @@ impl Night {
Ok(()) Ok(())
} }
pub const fn page(&self) -> Option<usize> {
match &self.night_state {
NightState::Active { current_page, .. } => Some(*current_page),
NightState::Complete => None,
}
}
pub fn received_response(&mut self, resp: ActionResponse) -> Result<ServerAction> { pub fn received_response(&mut self, resp: ActionResponse) -> Result<ServerAction> {
match self.received_response_with_role_blocks(resp)? { match self.received_response_with_role_blocks(resp)? {
BlockResolvedOutcome::PromptUpdate(prompt) => match &mut self.night_state { BlockResolvedOutcome::PromptUpdate(prompt) => match &mut self.night_state {
@ -660,7 +643,7 @@ impl Night {
} => current_result.replace(result.clone()), } => current_result.replace(result.clone()),
NightState::Complete => return Err(GameError::NightOver), NightState::Complete => return Err(GameError::NightOver),
}; };
if let NightChange::Shapeshift { source, .. } = &change { if let NightChange::Shapeshift { source, into } = &change {
// needs to be resolved _now_ so that the target can be woken // needs to be resolved _now_ so that the target can be woken
// for the role change with the wolves // for the role change with the wolves
self.apply_shapeshift(source)?; self.apply_shapeshift(source)?;
@ -709,7 +692,7 @@ impl Night {
) -> Result<ResponseOutcome> { ) -> Result<ResponseOutcome> {
let (current_cover, current_wolfy) = self let (current_cover, current_wolfy) = self
.current_prompt() .current_prompt()
.map(|(current_prompt, _)| { .map(|current_prompt| {
( (
*current_prompt == ActionPrompt::CoverOfDarkness, *current_prompt == ActionPrompt::CoverOfDarkness,
current_prompt.is_wolfy(), current_prompt.is_wolfy(),
@ -769,12 +752,7 @@ impl Night {
match self.received_response_consecutive_wolves_dont_sleep(resp)? { match self.received_response_consecutive_wolves_dont_sleep(resp)? {
ResponseOutcome::PromptUpdate(update) => Ok(BlockResolvedOutcome::PromptUpdate(update)), ResponseOutcome::PromptUpdate(update) => Ok(BlockResolvedOutcome::PromptUpdate(update)),
ResponseOutcome::ActionComplete(ActionComplete { result, change }) => { ResponseOutcome::ActionComplete(ActionComplete { result, change }) => {
match self match self.current_prompt().ok_or(GameError::NightOver)?.unless() {
.current_prompt()
.ok_or(GameError::NightOver)?
.0
.unless()
{
Some(Unless::TargetBlocked(unless_blocked)) => { Some(Unless::TargetBlocked(unless_blocked)) => {
if self.changes_from_actions().into_iter().any(|c| match c { if self.changes_from_actions().into_iter().any(|c| match c {
NightChange::RoleBlock { NightChange::RoleBlock {
@ -1307,14 +1285,13 @@ impl Night {
} }
} }
pub const fn current_prompt(&self) -> Option<(&ActionPrompt, usize)> { pub const fn current_prompt(&self) -> Option<&ActionPrompt> {
match &self.night_state { match &self.night_state {
NightState::Active { NightState::Active {
current_prompt, current_prompt,
current_result: _, current_result: _,
current_page,
.. ..
} => Some((current_prompt, *current_page)), } => Some(current_prompt),
NightState::Complete => None, NightState::Complete => None,
} }
} }
@ -1374,7 +1351,6 @@ impl Night {
current_prompt, current_prompt,
current_result: Some(result), current_result: Some(result),
current_changes, current_changes,
..
} => { } => {
self.used_actions.push(( self.used_actions.push((
current_prompt.clone(), current_prompt.clone(),
@ -1401,7 +1377,6 @@ impl Night {
current_prompt: prompt, current_prompt: prompt,
current_result: None, current_result: None,
current_changes: Vec::new(), current_changes: Vec::new(),
current_page: 0,
}; };
} else { } else {
self.night_state = NightState::Complete; self.night_state = NightState::Complete;
@ -1559,13 +1534,6 @@ impl Night {
.collect(), .collect(),
) )
} }
pub fn next_page(&mut self) {
match &mut self.night_state {
NightState::Active { current_page, .. } => *current_page += 1,
_ => {}
}
}
} }
pub enum ServerAction { pub enum ServerAction {

View File

@ -1,5 +1,4 @@
use core::num::NonZeroU8; use core::num::NonZeroU8;
use std::{rc::Rc, sync::Arc};
use rand::Rng; use rand::Rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -19,7 +18,6 @@ use crate::{
pub struct Village { pub struct Village {
characters: Box<[Character]>, characters: Box<[Character]>,
date_time: DateTime, date_time: DateTime,
settings: GameSettings,
} }
impl Village { impl Village {
@ -35,16 +33,11 @@ impl Village {
characters.sort_by_key(|l| l.number()); characters.sort_by_key(|l| l.number());
Ok(Self { Ok(Self {
settings,
characters, characters,
date_time: DateTime::Night { number: 0 }, date_time: DateTime::Night { number: 0 },
}) })
} }
pub fn settings(&self) -> GameSettings {
self.settings.clone()
}
pub fn killing_wolf(&self) -> Option<&Character> { pub fn killing_wolf(&self) -> Option<&Character> {
let wolves = self.characters.iter().filter(|c| c.is_wolf()); let wolves = self.characters.iter().filter(|c| c.is_wolf());

View File

@ -209,7 +209,6 @@ impl ServerToHostMessageExt for ServerToHostMessage {
characters, characters,
marked, marked,
day, day,
..
} => (characters, marked, day), } => (characters, marked, day),
resp => panic!("expected daytime, got {resp:?}"), resp => panic!("expected daytime, got {resp:?}"),
} }
@ -217,8 +216,12 @@ impl ServerToHostMessageExt for ServerToHostMessage {
fn prompt(self) -> ActionPrompt { fn prompt(self) -> ActionPrompt {
match self { match self {
Self::ActionPrompt(prompt, _) => prompt, Self::ActionPrompt(prompt) => prompt,
Self::Daytime { .. } => panic!("{}", "[got daytime]".bold().red()), Self::Daytime {
characters: _,
marked: _,
day: _,
} => panic!("{}", "[got daytime]".bold().red()),
msg => panic!("expected server message <<{msg:?}>> to be an ActionPrompt"), msg => panic!("expected server message <<{msg:?}>> to be an ActionPrompt"),
} }
} }
@ -444,7 +447,6 @@ impl GameExt for Game {
characters, characters,
marked, marked,
day, day,
..
} => (characters, marked, day), } => (characters, marked, day),
res => panic!("unexpected response to next_expect_day: {res:?}"), res => panic!("unexpected response to next_expect_day: {res:?}"),
} }
@ -472,7 +474,6 @@ impl GameExt for Game {
characters, characters,
marked, marked,
day, day,
..
} => (characters, marked, day), } => (characters, marked, day),
res => panic!("unexpected response to mark_for_execution: {res:?}"), res => panic!("unexpected response to mark_for_execution: {res:?}"),
} }
@ -546,7 +547,7 @@ fn starts_with_wolf_intro() {
let resp = game.process(HostGameMessage::GetState).unwrap(); let resp = game.process(HostGameMessage::GetState).unwrap();
assert_eq!( assert_eq!(
resp, resp,
ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness, 0) ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness)
) )
} }
@ -577,7 +578,7 @@ fn no_wolf_kill_n1() {
assert!(matches!( assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next)) game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(), .unwrap(),
ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }, 0) ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ })
)); ));
assert_eq!( assert_eq!(
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
@ -586,7 +587,15 @@ fn no_wolf_kill_n1() {
.unwrap(), .unwrap(),
ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep), ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep),
); );
game.next_expect_day(); assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(),
ServerToHostMessage::Daytime {
characters: _,
marked: _,
day: _,
}
));
} }
#[test] #[test]
@ -607,7 +616,7 @@ fn yes_wolf_kill_n2() {
assert!(matches!( assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next)) game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(), .unwrap(),
ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ }, 0) ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ })
)); ));
assert_eq!( assert_eq!(
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
@ -617,7 +626,15 @@ fn yes_wolf_kill_n2() {
.result(), .result(),
ActionResult::GoBackToSleep, ActionResult::GoBackToSleep,
); );
game.next_expect_day(); assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(),
ServerToHostMessage::Daytime {
characters: _,
marked: _,
day: _,
}
));
let execution_target = game let execution_target = game
.village() .village()
@ -636,7 +653,6 @@ fn yes_wolf_kill_n2() {
characters: _, characters: _,
marked, marked,
day: _, day: _,
..
} => assert_eq!(marked.to_vec(), vec![execution_target]), } => assert_eq!(marked.to_vec(), vec![execution_target]),
resp => panic!("unexpected server message: {resp:#?}"), resp => panic!("unexpected server message: {resp:#?}"),
} }
@ -644,7 +660,7 @@ fn yes_wolf_kill_n2() {
assert_eq!( assert_eq!(
game.process(HostGameMessage::Day(HostDayMessage::Execute)) game.process(HostGameMessage::Day(HostDayMessage::Execute))
.unwrap(), .unwrap(),
ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness, 0) ServerToHostMessage::ActionPrompt(ActionPrompt::CoverOfDarkness)
); );
assert_eq!( assert_eq!(
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
@ -657,13 +673,10 @@ fn yes_wolf_kill_n2() {
assert!(matches!( assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next)) game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(), .unwrap(),
ServerToHostMessage::ActionPrompt( ServerToHostMessage::ActionPrompt(ActionPrompt::WolfPackKill {
ActionPrompt::WolfPackKill { living_villagers: _,
living_villagers: _, marked: _,
marked: _, })
},
0
)
)); ));
} }
@ -707,7 +720,6 @@ fn wolfpack_kill_all_targets_valid() {
characters: _, characters: _,
marked, marked,
day: _, day: _,
..
} => assert_eq!(marked.to_vec(), vec![execution_target]), } => assert_eq!(marked.to_vec(), vec![execution_target]),
resp => panic!("unexpected server message: {resp:#?}"), resp => panic!("unexpected server message: {resp:#?}"),
} }

View File

@ -48,14 +48,11 @@ fn previous_shapeshifter_undone_redone() {
} }
assert_eq!( assert_eq!(
game.get_state(), game.get_state(),
ServerToHostMessage::ActionPrompt( ServerToHostMessage::ActionPrompt(ActionPrompt::Shapeshifter {
ActionPrompt::Shapeshifter { character_id: game
character_id: game .character_by_player_id(shapeshifter_player_id)
.character_by_player_id(shapeshifter_player_id) .identity()
.identity() })
},
0
)
); );
game.response(ActionResponse::Shapeshift).r#continue(); game.response(ActionResponse::Shapeshift).r#continue();
assert_eq!( assert_eq!(
@ -107,14 +104,11 @@ fn previous_shapeshifter_undone_and_changed_to_no() {
} }
assert_eq!( assert_eq!(
game.get_state(), game.get_state(),
ServerToHostMessage::ActionPrompt( ServerToHostMessage::ActionPrompt(ActionPrompt::Shapeshifter {
ActionPrompt::Shapeshifter { character_id: game
character_id: game .character_by_player_id(shapeshifter_player_id)
.character_by_player_id(shapeshifter_player_id) .identity()
.identity() })
},
0
)
); );
game.r#continue().sleep(); game.r#continue().sleep();

View File

@ -40,7 +40,11 @@ fn protect_stops_shapeshift() {
.unwrap(), .unwrap(),
ServerToHostMessage::ActionResult(None, ActionResult::Continue) ServerToHostMessage::ActionResult(None, ActionResult::Continue)
); );
game.next().title().wolves_intro(); assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(),
ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ })
));
assert_eq!( assert_eq!(
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse( game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
ActionResponse::Continue ActionResponse::Continue
@ -48,7 +52,15 @@ fn protect_stops_shapeshift() {
.unwrap(), .unwrap(),
ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep), ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep),
); );
game.next_expect_day(); assert!(matches!(
game.process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap(),
ServerToHostMessage::Daytime {
characters: _,
marked: _,
day: _,
}
));
let execution_target = game let execution_target = game
.village() .village()
@ -63,9 +75,11 @@ fn protect_stops_shapeshift() {
))) )))
.unwrap() .unwrap()
{ {
ServerToHostMessage::Daytime { marked, .. } => { ServerToHostMessage::Daytime {
assert_eq!(marked.to_vec(), vec![execution_target]) characters: _,
} marked,
day: _,
} => assert_eq!(marked.to_vec(), vec![execution_target]),
resp => panic!("unexpected server message: {resp:#?}"), resp => panic!("unexpected server message: {resp:#?}"),
} }
@ -82,14 +96,11 @@ fn protect_stops_shapeshift() {
.process(HostGameMessage::Night(HostNightMessage::Next)) .process(HostGameMessage::Night(HostNightMessage::Next))
.unwrap() .unwrap()
{ {
ServerToHostMessage::ActionPrompt( ServerToHostMessage::ActionPrompt(ActionPrompt::Protector {
ActionPrompt::Protector { character_id: prot_char_id,
character_id: prot_char_id, targets,
targets, marked: None,
marked: None, }) => (
},
0,
) => (
targets targets
.into_iter() .into_iter()
.map(|c| game.village().character_by_id(c.character_id).unwrap()) .map(|c| game.village().character_by_id(c.character_id).unwrap())
@ -113,12 +124,9 @@ fn protect_stops_shapeshift() {
))) )))
.unwrap() .unwrap()
{ {
ServerToHostMessage::ActionPrompt( ServerToHostMessage::ActionPrompt(ActionPrompt::Protector {
ActionPrompt::Protector { marked: Some(mark), ..
marked: Some(mark), .. }) => assert_eq!(mark, prot_and_wolf_target, "marked target"),
},
0,
) => assert_eq!(mark, prot_and_wolf_target, "marked target"),
resp => panic!("unexpected response: {resp:?}"), resp => panic!("unexpected response: {resp:?}"),
} }

View File

@ -37,7 +37,6 @@ pub enum HostGameMessage {
pub enum HostNightMessage { pub enum HostNightMessage {
ActionResponse(ActionResponse), ActionResponse(ActionResponse),
Next, Next,
NextPage,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@ -70,9 +69,8 @@ pub enum ServerToHostMessage {
characters: Box<[CharacterState]>, characters: Box<[CharacterState]>,
marked: Box<[CharacterId]>, marked: Box<[CharacterId]>,
day: NonZeroU8, day: NonZeroU8,
settings: GameSettings,
}, },
ActionPrompt(ActionPrompt, usize), ActionPrompt(ActionPrompt),
ActionResult(Option<CharacterIdentity>, ActionResult), ActionResult(Option<CharacterIdentity>, ActionResult),
Lobby(Box<[PlayerState]>), Lobby(Box<[PlayerState]>),
QrMode(bool), QrMode(bool),

View File

@ -1,14 +1,11 @@
use core::{ use core::ops::{Deref, DerefMut};
num::NonZeroU8,
ops::{Deref, DerefMut},
};
use tokio::sync::broadcast::{self, Sender}; use tokio::sync::broadcast::Sender;
use werewolves_proto::{ use werewolves_proto::{
error::GameError, error::GameError,
game::{Game, GameSettings}, game::{Game, GameSettings},
message::{ message::{
ClientMessage, Identification, PlayerState, PublicIdentity, ServerMessage, ClientMessage, Identification, PlayerState, ServerMessage,
host::{HostLobbyMessage, HostMessage, ServerToHostMessage}, host::{HostLobbyMessage, HostMessage, ServerToHostMessage},
}, },
player::PlayerId, player::PlayerId,
@ -338,27 +335,6 @@ impl DerefMut for LobbyPlayers {
} }
impl 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<ServerMessage>> { pub fn find(&self, player_id: PlayerId) -> Option<&Sender<ServerMessage>> {
self.iter() self.iter()
.find_map(|(id, s)| (id.player_id == player_id).then_some(s)) .find_map(|(id, s)| (id.player_id == player_id).then_some(s))

View File

@ -1,4 +1,4 @@
use core::{num::NonZeroU8, time::Duration}; use core::time::Duration;
use std::sync::Arc; use std::sync::Arc;
use werewolves_proto::{ use werewolves_proto::{
@ -11,7 +11,7 @@ use crate::{
communication::lobby::LobbyComms, communication::lobby::LobbyComms,
connection::JoinedPlayers, connection::JoinedPlayers,
game::{GameEnd, GameRunner}, game::{GameEnd, GameRunner},
lobby::{Lobby, LobbyPlayers}, lobby::Lobby,
saver::Saver, saver::Saver,
}; };
@ -22,13 +22,7 @@ pub struct IdentifiedClientMessage {
} }
pub async fn run_game(joined_players: JoinedPlayers, comms: LobbyComms, mut saver: impl Saver) { pub async fn run_game(joined_players: JoinedPlayers, comms: LobbyComms, mut saver: impl Saver) {
let mut lobby = Lobby::new(joined_players, comms); let mut state = RunningState::Lobby(Lobby::new(joined_players, comms));
if let Some(dummies) = option_env!("DUMMY_PLAYERS").and_then(|p| p.parse::<NonZeroU8>().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 { loop {
match &mut state { match &mut state {
RunningState::Lobby(lobby) => { RunningState::Lobby(lobby) => {

View File

@ -24,15 +24,15 @@
inkscape:deskcolor="#d1d1d1" inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm" inkscape:document-units="mm"
showgrid="true" showgrid="true"
inkscape:zoom="2.8284271" inkscape:zoom="1"
inkscape:cx="190.03495" inkscape:cx="71.5"
inkscape:cy="871.68588" inkscape:cy="601.5"
inkscape:window-width="1918" inkscape:window-width="1918"
inkscape:window-height="1042" inkscape:window-height="1042"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="17" inkscape:window-y="17"
inkscape:window-maximized="0" inkscape:window-maximized="0"
inkscape:current-layer="layer4"><inkscape:page inkscape:current-layer="layer3"><inkscape:page
x="0" x="0"
y="0" y="0"
width="52.348" width="52.348"
@ -40,22 +40,7 @@
id="page2" id="page2"
margin="0" margin="0"
bleed="0" /></sodipodi:namedview><defs bleed="0" /></sodipodi:namedview><defs
id="defs1"><inkscape:path-effect id="defs1" /><g
effect="mirror_symmetry"
start_point="41.438814,242.41673"
end_point="41.438814,281.85261"
center_point="41.438814,262.13467"
id="path-effect74"
is_visible="true"
lpeversion="1.2"
lpesatellites=""
mode="free"
discard_orig_path="false"
fuse_paths="false"
oposite_fuse="false"
split_items="false"
split_open="false"
link_styles="false" /></defs><g
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer4" id="layer4"
inkscape:label="Layer 4" inkscape:label="Layer 4"
@ -67,18 +52,13 @@
r="25" r="25"
inkscape:export-filename="../../src/werewolves/werewolves/img/wolf.svg" inkscape:export-filename="../../src/werewolves/werewolves/img/wolf.svg"
inkscape:export-xdpi="900.08" inkscape:export-xdpi="900.08"
inkscape:export-ydpi="900.08" /><g inkscape:export-ydpi="900.08" /><path
id="g74" id="path52"
inkscape:path-effect="#path-effect74" style="fill:#0f07ff;fill-opacity:1;stroke:#0f07ff;stroke-width:0.646547;stroke-dasharray:none;stroke-opacity:1"
transform="translate(16.485186,-41.489963)"><path inkscape:transform-center-x="0.38117126"
d="m 41.438814,253.85737 v -11.44064 c -0.10314,0.002 -0.206411,0.006 -0.309542,0.0114 -1.713539,0.0921 -3.402067,0.64565 -4.275191,1.61799 -1.746251,1.94469 -1.477015,6.95296 0.467672,8.69921 0.577742,0.51878 1.869333,0.91432 2.82205,1.08623 0.05301,0.04 0.6259,0.0432 1.295011,0.0258 z m 0,0 v -11.44064 c 0.10314,0.002 0.206411,0.006 0.309542,0.0114 1.713539,0.0921 3.402067,0.64565 4.275191,1.61799 1.746251,1.94469 1.477015,6.95296 -0.467672,8.69921 -0.577742,0.51878 -1.869333,0.91432 -2.82205,1.08623 -0.05301,0.04 -0.6259,0.0432 -1.295011,0.0258 z" inkscape:transform-center-y="-3.2844551"
style="fill:#0f07ff;stroke:#0f07ff;stroke-width:0.646547" d="m 57.107952,201.21922 c -1.713539,0.0921 -3.402067,0.64565 -4.275191,1.61799 -1.746251,1.94469 -1.477016,6.95297 0.467671,8.69922 0.577742,0.51878 1.869738,0.91451 2.822455,1.08642 0.645263,0.48645 0.441435,1.16866 0.235128,1.54824 -2.113413,0.40992 -4.423609,1.57646 -5.591796,3.31071 -3.059556,4.54209 -0.05893,15.93547 0,21.41161 0.01685,1.56554 14.736774,1.63168 14.555185,-0.0661 -0.582408,-5.44541 2.927265,-16.80337 -0.132292,-21.34547 -1.319465,-1.95883 -3.263349,-3.04974 -5.692251,-3.31904 -0.233696,-0.43409 -0.260906,-1.13472 0.150896,-1.67225 1.115403,-0.2586 1.751648,-0.75274 2.352402,-1.42176 1.746251,-1.94469 1.477016,-6.95348 -0.467672,-8.69973 -0.972343,-0.87312 -2.710997,-1.24193 -4.424535,-1.1498 z"
id="path73" sodipodi:nodetypes="sssccssssccssss" /><path
inkscape:original-d="m 41.438814,253.85737 v -11.44064 c -0.10314,0.002 -0.206411,0.006 -0.309542,0.0114 -1.713539,0.0921 -3.402067,0.64565 -4.275191,1.61799 -1.746251,1.94469 -1.477015,6.95296 0.467672,8.69921 0.577742,0.51878 1.869333,0.91432 2.82205,1.08623 0.05301,0.04 0.6259,0.0432 1.295011,0.0258 z" /><path
d="m 41.437781,281.85256 v -26.00719 c -0.537817,0.003 -1.074156,0.0344 -1.599386,0.0879 -2.633998,0.53128 -5.422716,2.05847 -6.299357,4.75888 -1.013707,3.1868 -0.65843,6.59772 -0.366903,9.8702 0.3233,3.36773 0.971622,6.70664 1.07487,10.09344 0.63992,0.8794 1.985837,0.77429 2.985348,0.97565 1.39519,0.12946 2.800457,0.21075 4.205428,0.22117 z m 0.0021,0 v -26.00719 c 0.537817,0.003 1.074156,0.0344 1.599386,0.0879 2.633998,0.53128 5.422716,2.05847 6.299357,4.75888 1.013707,3.1868 0.65843,6.59772 0.366903,9.8702 -0.3233,3.36773 -0.971622,6.70664 -1.07487,10.09344 -0.63992,0.8794 -1.985837,0.77429 -2.985348,0.97565 -1.39519,0.12946 -2.800457,0.21075 -4.205428,0.22117 z"
style="fill:#0f07ff;stroke:#0f07ff;stroke-width:0.646547"
id="path74"
inkscape:original-d="m 41.437781,281.85256 v -26.00719 c -0.537817,0.003 -1.074156,0.0344 -1.599386,0.0879 -2.633998,0.53128 -5.422716,2.05847 -6.299357,4.75888 -1.013707,3.1868 -0.65843,6.59772 -0.366903,9.8702 0.3233,3.36773 0.971622,6.70664 1.07487,10.09344 0.63992,0.8794 1.985837,0.77429 2.985348,0.97565 1.39519,0.12946 2.800457,0.21075 4.205428,0.22117 z" /></g><path
id="rect55" id="rect55"
style="display:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.7;stroke-dasharray:none;stroke-opacity:1" style="display:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.7;stroke-dasharray:none;stroke-opacity:1"
d="m 54.680079,205.91192 0.0075,3.95139 h -0.02186 v 1.21435 h 2.712393 v 25.2552 l 0.5302,0.68461 0.58136,-0.68461 v -25.2552 h 2.712392 v -1.21435 h -0.02232 l -0.007,-3.95139 -0.404006,-1.44994 -0.408967,1.44994 0.007,3.95139 h -1.877095 -0.149294 l -0.0075,-3.95139 -0.440632,-1.43824 -0.372342,1.43824 0.007,3.95139 H 57.37808 55.50052 l -0.007,-3.95139 -0.442679,-1.43824 z" d="m 54.680079,205.91192 0.0075,3.95139 h -0.02186 v 1.21435 h 2.712393 v 25.2552 l 0.5302,0.68461 0.58136,-0.68461 v -25.2552 h 2.712392 v -1.21435 h -0.02232 l -0.007,-3.95139 -0.404006,-1.44994 -0.408967,1.44994 0.007,3.95139 h -1.877095 -0.149294 l -0.0075,-3.95139 -0.440632,-1.43824 -0.372342,1.43824 0.007,3.95139 H 57.37808 55.50052 l -0.007,-3.95139 -0.442679,-1.43824 z"

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -931,13 +931,6 @@ input {
$village_bg: color.change($village_color, $alpha: 0.3); $village_bg: color.change($village_color, $alpha: 0.3);
$village_border: color.change($village_color, $alpha: 1.0); $village_border: color.change($village_color, $alpha: 1.0);
.targets {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
}
.character { .character {
padding: 0.5cm; padding: 0.5cm;
@ -1312,15 +1305,3 @@ input {
flex-direction: column; flex-direction: column;
flex-wrap: nowrap; flex-wrap: nowrap;
} }
.page {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-items: center;
width: 100%;
height: 100%;
// width: ;
& .next {}
}

View File

@ -31,7 +31,6 @@ use crate::{
action::{ActionResultView, Prompt}, action::{ActionResultView, Prompt},
host::{DaytimePlayerList, Setup}, host::{DaytimePlayerList, Setup},
}, },
pages::RolePage,
storage::StorageKey, storage::StorageKey,
}; };
@ -197,7 +196,6 @@ pub enum HostState {
characters: Box<[CharacterState]>, characters: Box<[CharacterState]>,
marked_for_execution: Box<[CharacterId]>, marked_for_execution: Box<[CharacterId]>,
day: NonZeroU8, day: NonZeroU8,
settings: GameSettings,
}, },
GameOver { GameOver {
result: GameOver, result: GameOver,
@ -206,7 +204,7 @@ pub enum HostState {
ackd: Box<[CharacterIdentity]>, ackd: Box<[CharacterIdentity]>,
waiting: Box<[CharacterIdentity]>, waiting: Box<[CharacterIdentity]>,
}, },
Prompt(ActionPrompt, usize), Prompt(ActionPrompt),
Result(Option<CharacterIdentity>, ActionResult), Result(Option<CharacterIdentity>, ActionResult),
} }
@ -219,12 +217,10 @@ impl From<ServerToHostMessage> for HostEvent {
characters, characters,
day, day,
marked: marked_for_execution, marked: marked_for_execution,
settings,
} => HostEvent::SetState(HostState::Day { } => HostEvent::SetState(HostState::Day {
characters, characters,
day, day,
marked_for_execution, marked_for_execution,
settings,
}), }),
ServerToHostMessage::Lobby(players) => HostEvent::PlayerList(players), ServerToHostMessage::Lobby(players) => HostEvent::PlayerList(players),
ServerToHostMessage::GameSettings(settings) => HostEvent::Settings(settings), ServerToHostMessage::GameSettings(settings) => HostEvent::Settings(settings),
@ -232,8 +228,8 @@ impl From<ServerToHostMessage> for HostEvent {
ServerToHostMessage::GameOver(game_over) => { ServerToHostMessage::GameOver(game_over) => {
HostEvent::SetState(HostState::GameOver { result: game_over }) HostEvent::SetState(HostState::GameOver { result: game_over })
} }
ServerToHostMessage::ActionPrompt(prompt, page) => { ServerToHostMessage::ActionPrompt(prompt) => {
HostEvent::SetState(HostState::Prompt(prompt, page)) HostEvent::SetState(HostState::Prompt(prompt))
} }
ServerToHostMessage::ActionResult(_, ActionResult::Continue) => HostEvent::Continue, ServerToHostMessage::ActionResult(_, ActionResult::Continue) => HostEvent::Continue,
ServerToHostMessage::ActionResult(ident, result) => { ServerToHostMessage::ActionResult(ident, result) => {
@ -313,7 +309,7 @@ impl Component for Host {
}, },
HostState::Lobby { players, settings } => { HostState::Lobby { players, settings } => {
if self.big_screen { if self.big_screen {
self.lobby_big_screen_show_setup(settings) self.lobby_big_screen_show_setup(players, settings)
} else { } else {
self.lobby_setup(players, settings) self.lobby_setup(players, settings)
} }
@ -322,35 +318,30 @@ impl Component for Host {
characters, characters,
day, day,
marked_for_execution, marked_for_execution,
settings,
} => { } => {
if self.big_screen { let on_mark = crate::callback::send_fn(
self.lobby_big_screen_show_setup(settings) |target| {
} else { HostMessage::InGame(HostGameMessage::Day(HostDayMessage::MarkForExecution(
let on_mark = crate::callback::send_fn( target,
|target| { )))
HostMessage::InGame(HostGameMessage::Day( },
HostDayMessage::MarkForExecution(target), self.send.clone(),
)) );
}, let on_execute = crate::callback::send_message(
self.send.clone(), HostMessage::InGame(HostGameMessage::Day(HostDayMessage::Execute)),
); self.send.clone(),
let on_execute = crate::callback::send_message( );
HostMessage::InGame(HostGameMessage::Day(HostDayMessage::Execute)), html! {
self.send.clone(), <DaytimePlayerList
); day={day}
html! { marked={marked_for_execution}
<DaytimePlayerList big_screen={self.big_screen}
day={day} characters={
marked={marked_for_execution} characters.into_iter().map(|c| c.into()).collect::<Box<[_]>>()
big_screen={self.big_screen} }
characters={ on_execute={on_execute}
characters.into_iter().map(|c| c.into()).collect::<Box<[_]>>() on_mark={on_mark}
} />
on_execute={on_execute}
on_mark={on_mark}
/>
}
} }
} }
HostState::RoleReveal { ackd, waiting } => { HostState::RoleReveal { ackd, waiting } => {
@ -373,7 +364,7 @@ impl Component for Host {
<RoleReveal ackd={ackd} waiting={waiting} on_force_ready={on_force_ready}/> <RoleReveal ackd={ackd} waiting={waiting} on_force_ready={on_force_ready}/>
} }
} }
HostState::Prompt(prompt, page) => { HostState::Prompt(prompt) => {
let send = self.send.clone(); let send = self.send.clone();
let on_complete = Callback::from(move |msg| { let on_complete = Callback::from(move |msg| {
let mut send = send.clone(); let mut send = send.clone();
@ -383,12 +374,9 @@ impl Component for Host {
} }
}); });
}); });
let pages = prompt.role_pages(self.big_screen);
html! { html! {
<Prompt <Prompt
pages={pages}
page_idx={page}
prompt={prompt} prompt={prompt}
big_screen={self.big_screen} big_screen={self.big_screen}
on_complete={on_complete} on_complete={on_complete}
@ -506,10 +494,17 @@ impl Component for Host {
}); });
} }
HostState::Prompt(_, _) HostState::Prompt(_)
| HostState::Result(_, _) | HostState::Result(_, _)
| HostState::RoleReveal { .. } | HostState::RoleReveal {
| HostState::Day { .. } => { ackd: _,
waiting: _,
}
| HostState::Day {
characters: _,
day: _,
marked_for_execution: _,
} => {
return false; return false;
} }
} }
@ -531,7 +526,7 @@ impl Component for Host {
*s = settings; *s = settings;
true true
} }
HostState::Prompt(_, _) HostState::Prompt(_)
| HostState::Result(_, _) | HostState::Result(_, _)
| HostState::Disconnected | HostState::Disconnected
| HostState::RoleReveal { | HostState::RoleReveal {
@ -539,7 +534,11 @@ impl Component for Host {
waiting: _, waiting: _,
} }
| HostState::GameOver { result: _ } | HostState::GameOver { result: _ }
| HostState::Day { .. } => { | HostState::Day {
characters: _,
day: _,
marked_for_execution: _,
} => {
log::info!("ignoring settings get"); log::info!("ignoring settings get");
false false
} }
@ -589,7 +588,7 @@ impl Component for Host {
} }
impl Host { impl Host {
fn lobby_big_screen_show_setup(&self, settings: GameSettings) -> Html { fn lobby_big_screen_show_setup(&self, _: Rc<[PlayerState]>, settings: GameSettings) -> Html {
if !self.qr_mode { if !self.qr_mode {
return html! { return html! {
<Setup settings={settings}/> <Setup settings={settings}/>

View File

@ -33,20 +33,15 @@ pub fn TargetPicker(
} }
}) })
.collect::<Html>(); .collect::<Html>();
let continue_button = continue_callback.clone().map(|continue_callback| {
html! {
<Button on_click={continue_callback}>
{"continue"}
</Button>
}
});
html! { html! {
<div class="target-picker"> <div class="target-picker">
<div class="targets"> <div class="targets">
{targets} {targets}
</div> </div>
{continue_button} <Button on_click={continue_callback.clone().unwrap_or_default()}>
{"continue"}
</Button>
</div> </div>
} }
} }

View File

@ -1,5 +1,4 @@
use core::ops::Not; use core::ops::Not;
use std::rc::Rc;
use werewolves_proto::{ use werewolves_proto::{
character::CharacterId, character::CharacterId,
@ -23,10 +22,6 @@ pub struct ActionPromptProps {
#[prop_or_default] #[prop_or_default]
pub big_screen: bool, pub big_screen: bool,
pub on_complete: Callback<HostMessage>, pub on_complete: Callback<HostMessage>,
#[prop_or_default]
pub page_idx: usize,
#[prop_or_default]
pub pages: Rc<[Html]>,
} }
fn identity_html(props: &ActionPromptProps, ident: Option<&CharacterIdentity>) -> Option<Html> { fn identity_html(props: &ActionPromptProps, ident: Option<&CharacterIdentity>) -> Option<Html> {
@ -46,31 +41,6 @@ fn identity_html(props: &ActionPromptProps, ident: Option<&CharacterIdentity>) -
#[function_component] #[function_component]
pub fn Prompt(props: &ActionPromptProps) -> Html { 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! {
<Button on_click={on_page_next} classes={classes!("next")}>
{"next"}
</Button>
}
});
html! {
<div class="page">
{page.clone()}
{next}
</div>
}
}) {
return page;
}
let on_complete = props.on_complete.clone(); let on_complete = props.on_complete.clone();
let continue_callback = props.big_screen.not().then(|| { let continue_callback = props.big_screen.not().then(|| {
Callback::from(move |_| { Callback::from(move |_| {

View File

@ -1,41 +0,0 @@
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! {
<Identity ident={Into::<PublicIdentity>::into(character_id)} />
}
});
Rc::new([
html! {
<>
{ident.clone()}
<BeholderPage1 />
</>
},
html! {
<>
{ident}
<BeholderPage2 />
</>
},
])
}
_ => Rc::new([]),
}
}
}

View File

@ -1,21 +0,0 @@
use yew::prelude::*;
#[function_component]
pub fn BeholderPage1() -> Html {
html! {
<div>
<h1>{"this is a beholder"}</h1>
<h2>{"there's information on this page"}</h2>
</div>
}
}
#[function_component]
pub fn BeholderPage2() -> Html {
html! {
<div>
<h1>{"more information on beholder"}</h1>
<h2>{"idk, hi?"}</h2>
</div>
}
}