refactored night actions a la day markings
This commit is contained in:
parent
cbeee94113
commit
5ad2831688
|
|
@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use crate::{
|
||||
error::GameError,
|
||||
game::night::Night,
|
||||
game::night::{Night, ServerAction},
|
||||
message::{
|
||||
CharacterState, Identification,
|
||||
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
||||
|
|
@ -133,7 +133,8 @@ impl Game {
|
|||
GameState::Night { night },
|
||||
HostGameMessage::Night(HostNightMessage::ActionResponse(resp)),
|
||||
) => match night.received_response(resp.clone()) {
|
||||
Ok(res) => Ok(ServerToHostMessage::ActionResult(
|
||||
Ok(ServerAction::Prompt(prompt)) => Ok(ServerToHostMessage::ActionPrompt(prompt)),
|
||||
Ok(ServerAction::Result(res)) => Ok(ServerToHostMessage::ActionResult(
|
||||
night.current_character().map(|c| c.identity()),
|
||||
res,
|
||||
)),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -68,6 +68,28 @@ impl Village {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn killing_wolf(&self) -> Option<&Character> {
|
||||
let wolves = self.characters.iter().filter(|c| c.is_wolf());
|
||||
|
||||
{
|
||||
let ww = wolves
|
||||
.clone()
|
||||
.filter(|w| matches!(w.role().title(), RoleTitle::Werewolf))
|
||||
.collect::<Box<[_]>>();
|
||||
if !ww.is_empty() {
|
||||
return Some(ww[rand::random_range(0..ww.len())]);
|
||||
}
|
||||
}
|
||||
{
|
||||
let wolves = wolves.collect::<Box<[_]>>();
|
||||
if wolves.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(wolves[rand::random_range(0..wolves.len())])
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn date_time(&self) -> DateTime {
|
||||
self.date_time
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ use crate::{
|
|||
game::{Game, GameSettings},
|
||||
message::{
|
||||
CharacterState, Identification, PublicIdentity,
|
||||
host::{
|
||||
HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage,
|
||||
ServerToHostMessageTitle,
|
||||
},
|
||||
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
||||
night::{ActionPrompt, ActionPromptTitle, ActionResponse, ActionResult},
|
||||
},
|
||||
player::{CharacterId, PlayerId},
|
||||
|
|
@ -20,12 +17,39 @@ use core::{num::NonZeroU8, ops::Range};
|
|||
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
|
||||
use std::io::Write;
|
||||
|
||||
trait ActionResultExt {
|
||||
fn sleep(&self);
|
||||
fn r#continue(&self);
|
||||
}
|
||||
|
||||
impl ActionResultExt for ActionResult {
|
||||
fn sleep(&self) {
|
||||
assert_eq!(*self, ActionResult::GoBackToSleep)
|
||||
}
|
||||
|
||||
fn r#continue(&self) {
|
||||
assert_eq!(*self, ActionResult::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
trait ServerToHostMessageExt {
|
||||
fn prompt(self) -> ActionPrompt;
|
||||
fn result(self) -> ActionResult;
|
||||
fn daytime(self) -> (Box<[CharacterState]>, Box<[CharacterId]>, NonZeroU8);
|
||||
}
|
||||
|
||||
impl ServerToHostMessageExt for ServerToHostMessage {
|
||||
fn daytime(self) -> (Box<[CharacterState]>, Box<[CharacterId]>, NonZeroU8) {
|
||||
match self {
|
||||
Self::Daytime {
|
||||
characters,
|
||||
marked,
|
||||
day,
|
||||
} => (characters, marked, day),
|
||||
resp => panic!("expected daytime, got {resp:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt(self) -> ActionPrompt {
|
||||
match self {
|
||||
Self::ActionPrompt(prompt) => prompt,
|
||||
|
|
@ -48,7 +72,10 @@ impl ServerToHostMessageExt for ServerToHostMessage {
|
|||
|
||||
trait GameExt {
|
||||
fn next(&mut self) -> ActionPrompt;
|
||||
fn r#continue(&mut self) -> ActionResult;
|
||||
fn next_expect_day(&mut self) -> (Box<[CharacterState]>, Box<[CharacterId]>, NonZeroU8);
|
||||
fn mark(&mut self, mark: &CharacterId) -> ActionPrompt;
|
||||
fn mark_and_check(&mut self, mark: &CharacterId, check: impl FnOnce(&ActionPrompt) -> bool);
|
||||
fn response(&mut self, resp: ActionResponse) -> ActionResult;
|
||||
fn execute(&mut self) -> ActionPrompt;
|
||||
fn mark_for_execution(
|
||||
|
|
@ -58,6 +85,29 @@ trait GameExt {
|
|||
}
|
||||
|
||||
impl GameExt for Game {
|
||||
fn r#continue(&mut self) -> ActionResult {
|
||||
self.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::Continue,
|
||||
)))
|
||||
.unwrap()
|
||||
.result()
|
||||
}
|
||||
|
||||
fn mark(&mut self, mark: &CharacterId) -> ActionPrompt {
|
||||
self.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::MarkTarget(mark.clone()),
|
||||
)))
|
||||
.unwrap()
|
||||
.prompt()
|
||||
}
|
||||
|
||||
fn mark_and_check(&mut self, mark: &CharacterId, check: impl FnOnce(&ActionPrompt) -> bool) {
|
||||
let prompt = self.mark(mark);
|
||||
if !check(&prompt) {
|
||||
panic!("unexpected prompt: {prompt:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn next(&mut self) -> ActionPrompt {
|
||||
self.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap()
|
||||
|
|
@ -179,7 +229,7 @@ fn no_wolf_kill_n1() {
|
|||
let mut game = Game::new(&players, settings).unwrap();
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
ActionResponse::Continue
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
||||
|
|
@ -191,7 +241,7 @@ fn no_wolf_kill_n1() {
|
|||
));
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::WolvesIntroAck
|
||||
ActionResponse::Continue
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep),
|
||||
|
|
@ -214,7 +264,7 @@ fn yes_wolf_kill_n2() {
|
|||
let mut game = Game::new(&players, settings).unwrap();
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
ActionResponse::Continue
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
||||
|
|
@ -226,7 +276,7 @@ fn yes_wolf_kill_n2() {
|
|||
));
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::WolvesIntroAck
|
||||
ActionResponse::Continue
|
||||
)))
|
||||
.unwrap()
|
||||
.result(),
|
||||
|
|
@ -271,7 +321,7 @@ fn yes_wolf_kill_n2() {
|
|||
);
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
ActionResponse::Continue
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
||||
|
|
@ -281,7 +331,8 @@ fn yes_wolf_kill_n2() {
|
|||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionPrompt(ActionPrompt::WolfPackKill {
|
||||
living_villagers: _
|
||||
living_villagers: _,
|
||||
marked: _,
|
||||
})
|
||||
));
|
||||
}
|
||||
|
|
@ -297,7 +348,7 @@ fn protect_stops_shapeshift() {
|
|||
let mut game = Game::new(&players, settings).unwrap();
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
ActionResponse::Continue,
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
||||
|
|
@ -309,7 +360,7 @@ fn protect_stops_shapeshift() {
|
|||
));
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::WolvesIntroAck
|
||||
ActionResponse::Continue
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep),
|
||||
|
|
@ -353,14 +404,7 @@ fn protect_stops_shapeshift() {
|
|||
.title(),
|
||||
ActionPromptTitle::CoverOfDarkness
|
||||
);
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
)))
|
||||
.unwrap()
|
||||
.result(),
|
||||
ActionResult::Continue
|
||||
);
|
||||
game.r#continue().r#continue();
|
||||
|
||||
let (prot_and_wolf_target, prot_char_id) = match game
|
||||
.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
|
|
@ -369,6 +413,7 @@ fn protect_stops_shapeshift() {
|
|||
ServerToHostMessage::ActionPrompt(ActionPrompt::Protector {
|
||||
character_id: prot_char_id,
|
||||
targets,
|
||||
marked: None,
|
||||
}) => (
|
||||
targets
|
||||
.into_iter()
|
||||
|
|
@ -388,55 +433,35 @@ fn protect_stops_shapeshift() {
|
|||
.clone();
|
||||
log::info!("target: {target:#?}");
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::Protector(prot_and_wolf_target.clone())
|
||||
match game
|
||||
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::MarkTarget(prot_and_wolf_target.clone()),
|
||||
)))
|
||||
.unwrap()
|
||||
.result(),
|
||||
ActionResult::GoBackToSleep,
|
||||
);
|
||||
{
|
||||
ServerToHostMessage::ActionPrompt(ActionPrompt::Protector {
|
||||
marked: Some(mark), ..
|
||||
}) => assert_eq!(mark, prot_and_wolf_target, "marked target"),
|
||||
resp => panic!("unexpected response: {resp:?}"),
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap()
|
||||
.prompt()
|
||||
.title(),
|
||||
ActionPromptTitle::WolfPackKill
|
||||
);
|
||||
game.r#continue().sleep();
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::WolfPackKillVote(prot_and_wolf_target.clone())
|
||||
)))
|
||||
.unwrap()
|
||||
.result(),
|
||||
ActionResult::Continue,
|
||||
);
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::WolfPackKill);
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap()
|
||||
.prompt()
|
||||
.title(),
|
||||
ActionPromptTitle::Shapeshifter,
|
||||
);
|
||||
game.mark_and_check(&prot_and_wolf_target, |c| match c {
|
||||
ActionPrompt::WolfPackKill {
|
||||
marked: Some(mark), ..
|
||||
} => prot_and_wolf_target == *mark,
|
||||
_ => false,
|
||||
});
|
||||
game.r#continue().r#continue();
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::Shapeshifter(true)
|
||||
)))
|
||||
.unwrap()
|
||||
.result(),
|
||||
ActionResult::GoBackToSleep,
|
||||
);
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::Shapeshifter,);
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap()
|
||||
.title(),
|
||||
ServerToHostMessageTitle::Daytime,
|
||||
);
|
||||
game.response(ActionResponse::Shapeshift);
|
||||
|
||||
game.next_expect_day();
|
||||
|
||||
let target = game
|
||||
.village()
|
||||
|
|
@ -462,34 +487,11 @@ fn wolfpack_kill_all_targets_valid() {
|
|||
settings.add(RoleTitle::Shapeshifter).unwrap();
|
||||
settings.sub(RoleTitle::Werewolf);
|
||||
let mut game = Game::new(&players, settings).unwrap();
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
||||
);
|
||||
assert!(matches!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionPrompt(ActionPrompt::WolvesIntro { wolves: _ })
|
||||
));
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::WolvesIntroAck
|
||||
)))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::ActionResult(None, ActionResult::GoBackToSleep),
|
||||
);
|
||||
assert!(matches!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap(),
|
||||
ServerToHostMessage::Daytime {
|
||||
characters: _,
|
||||
marked: _,
|
||||
day: _,
|
||||
}
|
||||
));
|
||||
game.r#continue().r#continue();
|
||||
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
|
||||
game.r#continue().sleep();
|
||||
game.next_expect_day();
|
||||
|
||||
let execution_target = game
|
||||
.village()
|
||||
|
|
@ -513,28 +515,18 @@ fn wolfpack_kill_all_targets_valid() {
|
|||
resp => panic!("unexpected server message: {resp:#?}"),
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Day(HostDayMessage::Execute))
|
||||
.unwrap()
|
||||
.prompt()
|
||||
.title(),
|
||||
ActionPromptTitle::CoverOfDarkness
|
||||
);
|
||||
assert_eq!(
|
||||
game.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::ClearCoverOfDarkness
|
||||
)))
|
||||
.unwrap()
|
||||
.result(),
|
||||
ActionResult::Continue
|
||||
);
|
||||
assert_eq!(game.execute().title(), ActionPromptTitle::CoverOfDarkness);
|
||||
game.r#continue().r#continue();
|
||||
|
||||
let living_villagers = match game
|
||||
.process(HostGameMessage::Night(HostNightMessage::Next))
|
||||
.unwrap()
|
||||
.prompt()
|
||||
{
|
||||
ActionPrompt::WolfPackKill { living_villagers } => living_villagers,
|
||||
ActionPrompt::WolfPackKill {
|
||||
living_villagers,
|
||||
marked: _,
|
||||
} => living_villagers,
|
||||
_ => panic!("not wolf pack kill"),
|
||||
};
|
||||
|
||||
|
|
@ -542,12 +534,14 @@ fn wolfpack_kill_all_targets_valid() {
|
|||
let mut attempt = game.clone();
|
||||
if let ServerToHostMessage::Error(GameError::InvalidTarget) = attempt
|
||||
.process(HostGameMessage::Night(HostNightMessage::ActionResponse(
|
||||
ActionResponse::WolfPackKillVote(target.character_id.clone()),
|
||||
ActionResponse::MarkTarget(target.character_id.clone()),
|
||||
)))
|
||||
.unwrap()
|
||||
{
|
||||
panic!("invalid target {target:?} at index [{idx}]");
|
||||
}
|
||||
attempt.r#continue().r#continue();
|
||||
assert_eq!(attempt.next().title(), ActionPromptTitle::Shapeshifter);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,15 +553,9 @@ fn only_1_shapeshift_prompt_if_first_shifts() {
|
|||
settings.add(RoleTitle::Shapeshifter).unwrap();
|
||||
settings.sub(RoleTitle::Werewolf);
|
||||
let mut game = Game::new(&players, settings).unwrap();
|
||||
assert_eq!(
|
||||
game.response(ActionResponse::ClearCoverOfDarkness),
|
||||
ActionResult::Continue
|
||||
);
|
||||
game.r#continue().r#continue();
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
|
||||
assert_eq!(
|
||||
game.response(ActionResponse::WolvesIntroAck),
|
||||
ActionResult::GoBackToSleep
|
||||
);
|
||||
game.r#continue().sleep();
|
||||
game.next_expect_day();
|
||||
let target = game
|
||||
.village()
|
||||
|
|
@ -579,10 +567,7 @@ fn only_1_shapeshift_prompt_if_first_shifts() {
|
|||
let (marked, target_list): (&[CharacterId], &[CharacterId]) = (&marked, &[target]);
|
||||
assert_eq!(target_list, marked);
|
||||
assert_eq!(game.execute().title(), ActionPromptTitle::CoverOfDarkness);
|
||||
assert_eq!(
|
||||
game.response(ActionResponse::ClearCoverOfDarkness),
|
||||
ActionResult::Continue
|
||||
);
|
||||
game.r#continue().r#continue();
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::WolfPackKill);
|
||||
let target = game
|
||||
.village()
|
||||
|
|
@ -590,20 +575,18 @@ fn only_1_shapeshift_prompt_if_first_shifts() {
|
|||
.into_iter()
|
||||
.find_map(|c| (c.is_village() && c.alive()).then_some(c.character_id().clone()))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
game.response(ActionResponse::WolfPackKillVote(target)),
|
||||
ActionResult::Continue,
|
||||
);
|
||||
|
||||
game.mark_and_check(&target, |p| match p {
|
||||
ActionPrompt::WolfPackKill {
|
||||
marked: Some(t), ..
|
||||
} => *t == target,
|
||||
_ => false,
|
||||
});
|
||||
game.r#continue().r#continue();
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::Shapeshifter);
|
||||
assert_eq!(
|
||||
game.response(ActionResponse::Shapeshifter(true)),
|
||||
ActionResult::Continue,
|
||||
);
|
||||
game.response(ActionResponse::Shapeshift).r#continue();
|
||||
assert_eq!(game.next().title(), ActionPromptTitle::RoleChange);
|
||||
assert_eq!(
|
||||
game.response(ActionResponse::RoleChangeAck),
|
||||
ActionResult::GoBackToSleep
|
||||
);
|
||||
game.r#continue().sleep();
|
||||
|
||||
game.next_expect_day();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,11 @@ fn night_order() {
|
|||
character_id: character_identity(),
|
||||
},
|
||||
ActionPrompt::WolfPackKill {
|
||||
marked: None,
|
||||
living_villagers: Box::new([]),
|
||||
},
|
||||
ActionPrompt::Protector {
|
||||
marked: None,
|
||||
character_id: character_identity(),
|
||||
targets: Box::new([]),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ use serde::{Deserialize, Serialize};
|
|||
use werewolves_macros::{ChecksAs, Titles};
|
||||
|
||||
use crate::{
|
||||
error::GameError,
|
||||
message::CharacterIdentity,
|
||||
player::CharacterId,
|
||||
role::{Alignment, PreviousGuardianAction, RoleTitle},
|
||||
};
|
||||
|
||||
type Result<T> = core::result::Result<T, GameError>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd)]
|
||||
pub enum ActionType {
|
||||
Cover,
|
||||
|
|
@ -37,7 +40,6 @@ pub enum ActionPrompt {
|
|||
#[checks(ActionType::Cover)]
|
||||
CoverOfDarkness,
|
||||
#[checks(ActionType::WolfPackKill)]
|
||||
#[checks]
|
||||
WolvesIntro {
|
||||
wolves: Box<[(CharacterIdentity, RoleTitle)]>,
|
||||
},
|
||||
|
|
@ -50,48 +52,57 @@ pub enum ActionPrompt {
|
|||
Seer {
|
||||
character_id: CharacterIdentity,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Protect)]
|
||||
Protector {
|
||||
character_id: CharacterIdentity,
|
||||
targets: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Other)]
|
||||
Arcanist {
|
||||
character_id: CharacterIdentity,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: (Option<CharacterId>, Option<CharacterId>),
|
||||
},
|
||||
#[checks(ActionType::Other)]
|
||||
Gravedigger {
|
||||
character_id: CharacterIdentity,
|
||||
dead_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Other)]
|
||||
Hunter {
|
||||
character_id: CharacterIdentity,
|
||||
current_target: Option<CharacterIdentity>,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Other)]
|
||||
Militia {
|
||||
character_id: CharacterIdentity,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Other)]
|
||||
MapleWolf {
|
||||
character_id: CharacterIdentity,
|
||||
kill_or_die: bool,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Protect)]
|
||||
Guardian {
|
||||
character_id: CharacterIdentity,
|
||||
previous: Option<PreviousGuardianAction>,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::WolfPackKill)]
|
||||
WolfPackKill {
|
||||
living_villagers: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::OtherWolf)]
|
||||
Shapeshifter { character_id: CharacterIdentity },
|
||||
|
|
@ -99,15 +110,147 @@ pub enum ActionPrompt {
|
|||
AlphaWolf {
|
||||
character_id: CharacterIdentity,
|
||||
living_villagers: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
#[checks(ActionType::Direwolf)]
|
||||
DireWolf {
|
||||
character_id: CharacterIdentity,
|
||||
living_players: Box<[CharacterIdentity]>,
|
||||
marked: Option<CharacterId>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ActionPrompt {
|
||||
pub fn with_mark(&self, mark: CharacterId) -> Result<ActionPrompt> {
|
||||
let mut prompt = self.clone();
|
||||
match &mut prompt {
|
||||
ActionPrompt::WolvesIntro { .. }
|
||||
| ActionPrompt::RoleChange { .. }
|
||||
| ActionPrompt::Shapeshifter { .. }
|
||||
| ActionPrompt::CoverOfDarkness => Err(GameError::InvalidMessageForGameState),
|
||||
|
||||
ActionPrompt::Guardian {
|
||||
previous,
|
||||
living_players,
|
||||
marked,
|
||||
..
|
||||
} => {
|
||||
if !living_players.iter().any(|c| c.character_id == mark)
|
||||
|| previous
|
||||
.as_ref()
|
||||
.and_then(|p| match p {
|
||||
PreviousGuardianAction::Protect(_) => None,
|
||||
PreviousGuardianAction::Guard(c) => Some(c.character_id == mark),
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
// not in target list OR guarded target previous night
|
||||
return Err(GameError::InvalidTarget);
|
||||
}
|
||||
match marked.as_mut() {
|
||||
Some(marked_cid) => {
|
||||
if marked_cid == &mark {
|
||||
marked.take();
|
||||
} else {
|
||||
marked.replace(mark);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
marked.replace(mark);
|
||||
}
|
||||
}
|
||||
Ok(prompt)
|
||||
}
|
||||
ActionPrompt::Arcanist {
|
||||
living_players: targets,
|
||||
marked,
|
||||
..
|
||||
} => {
|
||||
if !targets.iter().any(|t| t.character_id == mark) {
|
||||
return Err(GameError::InvalidTarget);
|
||||
}
|
||||
match marked {
|
||||
(None, Some(m)) | (Some(m), None) => {
|
||||
if *m == mark {
|
||||
*marked = (None, None);
|
||||
} else {
|
||||
*marked = (Some(m.clone()), Some(mark));
|
||||
}
|
||||
}
|
||||
(None, None) => *marked = (Some(mark), None),
|
||||
(Some(m1), Some(m2)) => {
|
||||
if *m1 == mark {
|
||||
*marked = (Some(m2.clone()), None);
|
||||
} else if *m2 == mark {
|
||||
*marked = (Some(m1.clone()), None);
|
||||
} else {
|
||||
*marked = (Some(m2.clone()), Some(mark));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(prompt)
|
||||
}
|
||||
|
||||
ActionPrompt::Protector {
|
||||
targets, marked, ..
|
||||
}
|
||||
| ActionPrompt::Seer {
|
||||
living_players: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::Gravedigger {
|
||||
dead_players: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::Hunter {
|
||||
living_players: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::Militia {
|
||||
living_players: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::MapleWolf {
|
||||
living_players: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::WolfPackKill {
|
||||
living_villagers: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::AlphaWolf {
|
||||
living_villagers: targets,
|
||||
marked,
|
||||
..
|
||||
}
|
||||
| ActionPrompt::DireWolf {
|
||||
living_players: targets,
|
||||
marked,
|
||||
..
|
||||
} => {
|
||||
if !targets.iter().any(|t| t.character_id == mark) {
|
||||
return Err(GameError::InvalidTarget);
|
||||
}
|
||||
if let Some(marked_char) = marked.as_ref()
|
||||
&& *marked_char == mark
|
||||
{
|
||||
marked.take();
|
||||
} else {
|
||||
marked.replace(mark);
|
||||
}
|
||||
|
||||
Ok(prompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_wolfy(&self) -> bool {
|
||||
self.action_type().is_wolfy()
|
||||
|| match self {
|
||||
|
|
@ -126,25 +269,22 @@ impl PartialOrd for ActionPrompt {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Deserialize, ChecksAs)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Deserialize)]
|
||||
pub enum ActionResponse {
|
||||
Seer(CharacterId),
|
||||
Arcanist(CharacterId, CharacterId),
|
||||
Gravedigger(CharacterId),
|
||||
Hunter(CharacterId),
|
||||
Militia(Option<CharacterId>),
|
||||
MapleWolf(Option<CharacterId>),
|
||||
Guardian(CharacterId),
|
||||
WolfPackKillVote(CharacterId),
|
||||
#[checks]
|
||||
Shapeshifter(bool),
|
||||
AlphaWolf(Option<CharacterId>),
|
||||
Direwolf(CharacterId),
|
||||
Protector(CharacterId),
|
||||
#[checks]
|
||||
RoleChangeAck,
|
||||
WolvesIntroAck,
|
||||
ClearCoverOfDarkness,
|
||||
// Seer(CharacterId),
|
||||
// Arcanist(Option<CharacterId>, Option<CharacterId>),
|
||||
// Gravedigger(CharacterId),
|
||||
// Hunter(CharacterId),
|
||||
// Militia(Option<CharacterId>),
|
||||
// MapleWolf(Option<CharacterId>),
|
||||
// Guardian(CharacterId),
|
||||
// WolfPackKillVote(CharacterId),
|
||||
// AlphaWolf(Option<CharacterId>),
|
||||
// Direwolf(CharacterId),
|
||||
// Protector(CharacterId),
|
||||
MarkTarget(CharacterId),
|
||||
Shapeshift,
|
||||
Continue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
game::{DateTime, Village},
|
||||
message::{CharacterIdentity, Identification, PublicIdentity, night::ActionPrompt},
|
||||
modifier::Modifier,
|
||||
role::{MAPLE_WOLF_ABSTAIN_LIMIT, PreviousGuardianAction, Role, RoleTitle},
|
||||
role::{Alignment, MAPLE_WOLF_ABSTAIN_LIMIT, PreviousGuardianAction, Role, RoleTitle},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
|
|
@ -173,6 +173,19 @@ impl Character {
|
|||
&self.role
|
||||
}
|
||||
|
||||
pub const fn gravedigger_dig(&self) -> Option<RoleTitle> {
|
||||
match &self.role {
|
||||
Role::Shapeshifter {
|
||||
shifted_into: Some(_),
|
||||
} => None,
|
||||
_ => Some(self.role.title()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn alignment(&self) -> Alignment {
|
||||
self.role.alignment()
|
||||
}
|
||||
|
||||
pub const fn role_mut(&mut self) -> &mut Role {
|
||||
&mut self.role
|
||||
}
|
||||
|
|
@ -222,22 +235,26 @@ impl Character {
|
|||
Role::Seer => ActionPrompt::Seer {
|
||||
character_id: self.identity(),
|
||||
living_players: village.living_players_excluding(self.character_id()),
|
||||
marked: None,
|
||||
},
|
||||
Role::Arcanist => ActionPrompt::Arcanist {
|
||||
character_id: self.identity(),
|
||||
living_players: village.living_players_excluding(self.character_id()),
|
||||
marked: (None, None),
|
||||
},
|
||||
Role::Protector {
|
||||
last_protected: Some(last_protected),
|
||||
} => ActionPrompt::Protector {
|
||||
character_id: self.identity(),
|
||||
targets: village.living_players_excluding(last_protected),
|
||||
marked: None,
|
||||
},
|
||||
Role::Protector {
|
||||
last_protected: None,
|
||||
} => ActionPrompt::Protector {
|
||||
character_id: self.identity(),
|
||||
targets: village.living_players_excluding(self.character_id()),
|
||||
marked: None,
|
||||
},
|
||||
Role::Apprentice(role) => {
|
||||
let current_night = match village.date_time() {
|
||||
|
|
@ -273,17 +290,21 @@ impl Character {
|
|||
Role::Militia { targeted: None } => ActionPrompt::Militia {
|
||||
character_id: self.identity(),
|
||||
living_players: village.living_players_excluding(self.character_id()),
|
||||
marked: None,
|
||||
},
|
||||
Role::Werewolf => ActionPrompt::WolfPackKill {
|
||||
living_villagers: village.living_players(),
|
||||
marked: None,
|
||||
},
|
||||
Role::AlphaWolf { killed: None } => ActionPrompt::AlphaWolf {
|
||||
character_id: self.identity(),
|
||||
living_villagers: village.living_players_excluding(self.character_id()),
|
||||
marked: None,
|
||||
},
|
||||
Role::DireWolf => ActionPrompt::DireWolf {
|
||||
character_id: self.identity(),
|
||||
living_players: village.living_players(),
|
||||
marked: None,
|
||||
},
|
||||
Role::Shapeshifter { shifted_into: None } => ActionPrompt::Shapeshifter {
|
||||
character_id: self.identity(),
|
||||
|
|
@ -291,16 +312,19 @@ impl Character {
|
|||
Role::Gravedigger => ActionPrompt::Gravedigger {
|
||||
character_id: self.identity(),
|
||||
dead_players: village.dead_targets(),
|
||||
marked: None,
|
||||
},
|
||||
Role::Hunter { target } => ActionPrompt::Hunter {
|
||||
character_id: self.identity(),
|
||||
current_target: target.as_ref().and_then(|t| village.target_by_id(t)),
|
||||
living_players: village.living_players_excluding(self.character_id()),
|
||||
marked: None,
|
||||
},
|
||||
Role::MapleWolf { last_kill_on_night } => ActionPrompt::MapleWolf {
|
||||
character_id: self.identity(),
|
||||
kill_or_die: last_kill_on_night + MAPLE_WOLF_ABSTAIN_LIMIT.get() == night,
|
||||
living_players: village.living_players_excluding(self.character_id()),
|
||||
marked: None,
|
||||
},
|
||||
Role::Guardian {
|
||||
last_protected: Some(PreviousGuardianAction::Guard(prev_target)),
|
||||
|
|
@ -308,6 +332,7 @@ impl Character {
|
|||
character_id: self.identity(),
|
||||
previous: Some(PreviousGuardianAction::Guard(prev_target.clone())),
|
||||
living_players: village.living_players_excluding(&prev_target.character_id),
|
||||
marked: None,
|
||||
},
|
||||
Role::Guardian {
|
||||
last_protected: Some(PreviousGuardianAction::Protect(prev_target)),
|
||||
|
|
@ -315,6 +340,7 @@ impl Character {
|
|||
character_id: self.identity(),
|
||||
previous: Some(PreviousGuardianAction::Protect(prev_target.clone())),
|
||||
living_players: village.living_players(),
|
||||
marked: None,
|
||||
},
|
||||
Role::Guardian {
|
||||
last_protected: None,
|
||||
|
|
@ -322,6 +348,7 @@ impl Character {
|
|||
character_id: self.identity(),
|
||||
previous: None,
|
||||
living_players: village.living_players(),
|
||||
marked: None,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@ struct Client {
|
|||
who: String,
|
||||
sender: Sender<IdentifiedClientMessage>,
|
||||
receiver: Receiver<ServerMessage>,
|
||||
// message_history: Vec<ServerMessage>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
|
|
@ -140,7 +139,6 @@ impl Client {
|
|||
who,
|
||||
sender,
|
||||
receiver,
|
||||
// message_history: Vec::new(),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "cbor")]
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ impl Lobby {
|
|||
.unwrap()
|
||||
.next_message()
|
||||
.await
|
||||
.expect("get next message");
|
||||
.expect("get next message"); // TODO: keeps happening
|
||||
|
||||
match self.next_inner(msg.clone()).await.map_err(|err| (msg, err)) {
|
||||
Ok(None) => {}
|
||||
|
|
|
|||
|
|
@ -863,7 +863,8 @@ input {
|
|||
|
||||
}
|
||||
|
||||
.character-picker {
|
||||
.character-picker,
|
||||
.target-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
|
@ -891,6 +892,16 @@ input {
|
|||
}
|
||||
}
|
||||
|
||||
&.dead {
|
||||
$bg: rgba(128, 128, 128, 0.5);
|
||||
background-color: $bg;
|
||||
border: 1px solid color.change($bg, $alpha: 1.0);
|
||||
|
||||
&:hover {
|
||||
background-color: color.change($bg, $alpha: 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
background-color: $village_bg;
|
||||
border: 1px solid $village_border;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
use werewolves_proto::{
|
||||
message::{CharacterIdentity, PublicIdentity},
|
||||
player::CharacterId,
|
||||
};
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::components::{Button, Identity};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
pub struct TargetPickerProps {
|
||||
pub targets: Box<[CharacterIdentity]>,
|
||||
pub marked: Box<[CharacterId]>,
|
||||
pub mark_callback: Option<Callback<CharacterId>>,
|
||||
pub continue_callback: Option<Callback<()>>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn TargetPicker(
|
||||
TargetPickerProps {
|
||||
targets,
|
||||
marked,
|
||||
mark_callback,
|
||||
continue_callback,
|
||||
}: &TargetPickerProps,
|
||||
) -> Html {
|
||||
let targets = targets
|
||||
.iter()
|
||||
.map(|t| {
|
||||
let cb = mark_callback.clone();
|
||||
let marked = marked.contains(&t.character_id);
|
||||
html! {
|
||||
<TargetCard target={t.clone()} marked={marked} mark_callback={cb}/>
|
||||
}
|
||||
})
|
||||
.collect::<Html>();
|
||||
|
||||
html! {
|
||||
<div class="target-picker">
|
||||
<div class="targets">
|
||||
{targets}
|
||||
</div>
|
||||
<Button on_click={continue_callback.clone().unwrap_or_default()}>
|
||||
{"continue"}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
pub struct TargetCardProps {
|
||||
pub target: CharacterIdentity,
|
||||
pub marked: bool,
|
||||
pub mark_callback: Option<Callback<CharacterId>>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn TargetCard(
|
||||
TargetCardProps {
|
||||
target,
|
||||
marked,
|
||||
mark_callback,
|
||||
}: &TargetCardProps,
|
||||
) -> Html {
|
||||
let click_target = target.character_id.clone();
|
||||
let on_click = mark_callback
|
||||
.clone()
|
||||
.map(|cb| Callback::from(move |_| cb.emit(click_target.clone())))
|
||||
.unwrap_or_default();
|
||||
let marked = marked.then_some("marked");
|
||||
let ident: PublicIdentity = target.into();
|
||||
html! {
|
||||
<Button on_click={on_click} classes={classes!(marked, "character")}>
|
||||
<Identity ident={ident}/>
|
||||
</Button>
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ use yew::prelude::*;
|
|||
|
||||
use crate::components::{
|
||||
Button, CoverOfDarkness, Identity,
|
||||
action::{BinaryChoice, OptionalSingleTarget, SingleTarget, TwoTarget, WolvesIntro},
|
||||
action::{BinaryChoice, TargetPicker, WolvesIntro},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
|
|
@ -41,241 +41,62 @@ fn identity_html(props: &ActionPromptProps, ident: Option<&CharacterIdentity>) -
|
|||
|
||||
#[function_component]
|
||||
pub fn Prompt(props: &ActionPromptProps) -> Html {
|
||||
match &props.prompt {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let continue_callback = props.big_screen.not().then(|| {
|
||||
Callback::from(move |_| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Continue),
|
||||
)))
|
||||
})
|
||||
});
|
||||
let on_complete = props.on_complete.clone();
|
||||
let mark_callback = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::MarkTarget(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
let (character_id, targets, marked, role_info) = match &props.prompt {
|
||||
ActionPrompt::CoverOfDarkness => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let next = props.big_screen.not().then(|| {
|
||||
Callback::from(move |_| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::ClearCoverOfDarkness),
|
||||
)))
|
||||
})
|
||||
});
|
||||
return html! {
|
||||
<CoverOfDarkness next={next} />
|
||||
<CoverOfDarkness next={continue_callback}/>
|
||||
};
|
||||
}
|
||||
ActionPrompt::WolvesIntro { wolves } => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_complete = Callback::from(move |_| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(
|
||||
werewolves_proto::message::night::ActionResponse::WolvesIntroAck,
|
||||
),
|
||||
)))
|
||||
});
|
||||
html! {
|
||||
return html! {
|
||||
<WolvesIntro
|
||||
big_screen={props.big_screen}
|
||||
on_complete={on_complete}
|
||||
on_complete={continue_callback}
|
||||
wolves={wolves.clone()}
|
||||
/>
|
||||
}
|
||||
}
|
||||
ActionPrompt::Seer {
|
||||
character_id,
|
||||
living_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Seer(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<SingleTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"check alignment"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
}
|
||||
ActionPrompt::RoleChange {
|
||||
character_id,
|
||||
new_role,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_click = Callback::from(move |_| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::RoleChangeAck),
|
||||
)))
|
||||
});
|
||||
let cont = props.big_screen.not().then(|| {
|
||||
let cont = continue_callback.map(|continue_callback| {
|
||||
html! {
|
||||
<Button on_click={on_click}>{"continue"}</Button>
|
||||
<Button on_click={continue_callback}>
|
||||
{"continue"}
|
||||
</Button>
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
return html! {
|
||||
<div class="role-change">
|
||||
{identity_html(props, Some(character_id))}
|
||||
<h2>{"your role has changed"}</h2>
|
||||
<p>{new_role.to_string()}</p>
|
||||
<h1>{new_role.to_string()}</h1>
|
||||
{cont}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::Protector {
|
||||
character_id,
|
||||
targets,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Protector(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<SingleTarget
|
||||
targets={targets.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"protector"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::Arcanist {
|
||||
character_id,
|
||||
living_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |(t1, t2): (CharacterId, CharacterId)| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Arcanist(t1, t2)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<TwoTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"arcanist"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::Gravedigger {
|
||||
character_id,
|
||||
dead_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Gravedigger(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<SingleTarget
|
||||
targets={dead_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"gravedigger"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::Hunter {
|
||||
character_id,
|
||||
current_target,
|
||||
living_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Hunter(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<SingleTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"hunter"}
|
||||
>
|
||||
<h3>
|
||||
<b>{"current target: "}</b>{current_target.clone().map(|t| html!{
|
||||
<Identity ident={Into::<PublicIdentity>::into(t)} />
|
||||
}).unwrap_or_else(|| html!{<i>{"none"}</i>})}
|
||||
</h3>
|
||||
</SingleTarget>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::Militia {
|
||||
character_id,
|
||||
living_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: Option<CharacterId>| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Militia(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<OptionalSingleTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"pew pew?"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::MapleWolf {
|
||||
character_id,
|
||||
kill_or_die,
|
||||
living_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: Option<CharacterId>| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::MapleWolf(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
let kill_or_die = kill_or_die.then(|| {
|
||||
html! {
|
||||
<em>{"if you fail to eat tonight, you will starve"}</em>
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<OptionalSingleTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"nom nom?"}
|
||||
>
|
||||
{kill_or_die}
|
||||
</OptionalSingleTarget>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ActionPrompt::Guardian {
|
||||
character_id,
|
||||
previous,
|
||||
living_players,
|
||||
marked,
|
||||
} => {
|
||||
let last_protect = previous.as_ref().map(|prev| match prev {
|
||||
PreviousGuardianAction::Protect(target) => {
|
||||
|
|
@ -293,108 +114,515 @@ pub fn Prompt(props: &ActionPromptProps) -> Html {
|
|||
</>
|
||||
},
|
||||
});
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then_some({
|
||||
move |prot| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Guardian(prot)),
|
||||
)));
|
||||
}
|
||||
});
|
||||
let marked = marked.iter().cloned().collect::<Box<[CharacterId]>>();
|
||||
|
||||
html! {
|
||||
return html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<SingleTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"pick someone to protect"}
|
||||
>
|
||||
{last_protect}
|
||||
</SingleTarget>
|
||||
{identity_html(props, Some(character_id))}
|
||||
<h2>{"guardian"}</h2>
|
||||
{last_protect}
|
||||
<TargetPicker
|
||||
targets={living_players.clone()}
|
||||
marked={marked}
|
||||
mark_callback={mark_callback}
|
||||
continue_callback={continue_callback}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
ActionPrompt::WolfPackKill { living_villagers } => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::WolfPackKillVote(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<SingleTarget
|
||||
targets={living_villagers.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"wolf pack kill"}
|
||||
/>
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ActionPrompt::Seer {
|
||||
character_id,
|
||||
living_players,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_players,
|
||||
marked.iter().cloned().collect::<Box<[CharacterId]>>(),
|
||||
html! {{"seer"}},
|
||||
),
|
||||
ActionPrompt::Protector {
|
||||
character_id,
|
||||
targets,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
targets,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {{"protector"}},
|
||||
),
|
||||
ActionPrompt::Arcanist {
|
||||
character_id,
|
||||
living_players,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_players,
|
||||
[&marked.0, &marked.1]
|
||||
.iter()
|
||||
.filter_map(|c| (*c).clone())
|
||||
.collect(),
|
||||
html! {{"arcanist"}},
|
||||
),
|
||||
ActionPrompt::Gravedigger {
|
||||
character_id,
|
||||
dead_players,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
dead_players,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {{"gravedigger"}},
|
||||
),
|
||||
ActionPrompt::Hunter {
|
||||
character_id,
|
||||
current_target,
|
||||
living_players,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_players,
|
||||
marked.iter().cloned().collect(),
|
||||
{
|
||||
let current_target = current_target.as_ref().cloned().map(|t| {
|
||||
html! {
|
||||
<>
|
||||
<h3>{"current target:"}</h3>
|
||||
<Identity ident={Into::<PublicIdentity>::into(t)} />
|
||||
</>
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<>
|
||||
<h2>{"hunter"}</h2>
|
||||
{current_target}
|
||||
</>
|
||||
}
|
||||
},
|
||||
),
|
||||
ActionPrompt::Militia {
|
||||
character_id,
|
||||
living_players,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_players,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {{"militia"}},
|
||||
),
|
||||
ActionPrompt::MapleWolf {
|
||||
character_id,
|
||||
kill_or_die,
|
||||
living_players,
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_players,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {<>{"maple wolf"} {kill_or_die.then_some(" — starving")}</>},
|
||||
),
|
||||
ActionPrompt::WolfPackKill {
|
||||
living_villagers,
|
||||
marked,
|
||||
} => (
|
||||
None,
|
||||
living_villagers,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {{"wolfpack kill"}},
|
||||
),
|
||||
ActionPrompt::Shapeshifter { character_id } => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then_some({
|
||||
move |shift| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Shapeshifter(shift)),
|
||||
HostNightMessage::ActionResponse(if shift {
|
||||
ActionResponse::Shapeshift
|
||||
} else {
|
||||
ActionResponse::Continue
|
||||
}),
|
||||
)));
|
||||
}
|
||||
});
|
||||
html! {
|
||||
return html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
{identity_html(props, Some(character_id))}
|
||||
<BinaryChoice on_chosen={on_select}>
|
||||
<h2>{"shapeshift?"}</h2>
|
||||
</BinaryChoice>
|
||||
</div>
|
||||
}
|
||||
};
|
||||
}
|
||||
ActionPrompt::AlphaWolf {
|
||||
character_id,
|
||||
living_villagers,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: Option<CharacterId>| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::AlphaWolf(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<OptionalSingleTarget
|
||||
targets={living_villagers.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"alpha wolf target"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_villagers,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {{"alpha wolf"}},
|
||||
),
|
||||
ActionPrompt::DireWolf {
|
||||
character_id,
|
||||
living_players,
|
||||
} => {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_select = props.big_screen.not().then(|| {
|
||||
Callback::from(move |target: CharacterId| {
|
||||
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
HostNightMessage::ActionResponse(ActionResponse::Direwolf(target)),
|
||||
)));
|
||||
})
|
||||
});
|
||||
html! {
|
||||
<div>
|
||||
{identity_html(props, Some(&character_id))}
|
||||
<SingleTarget
|
||||
targets={living_players.clone()}
|
||||
target_selection={on_select}
|
||||
headline={"direwolf block target"}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
marked,
|
||||
} => (
|
||||
Some(character_id),
|
||||
living_players,
|
||||
marked.iter().cloned().collect(),
|
||||
html! {{"dire wolf"}},
|
||||
),
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class="prompt">
|
||||
{identity_html(props, character_id)}
|
||||
<h2>{role_info}</h2>
|
||||
<TargetPicker
|
||||
targets={targets.clone()}
|
||||
marked={marked}
|
||||
mark_callback={mark_callback}
|
||||
continue_callback={continue_callback}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
// match &props.prompt {
|
||||
// ActionPrompt::CoverOfDarkness => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let next = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |_| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::ClearCoverOfDarkness),
|
||||
// )))
|
||||
// })
|
||||
// });
|
||||
// return html! {
|
||||
// <CoverOfDarkness next={next} />
|
||||
// };
|
||||
// }
|
||||
// ActionPrompt::WolvesIntro { wolves } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_complete = Callback::from(move |_| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(
|
||||
// werewolves_proto::message::night::ActionResponse::WolvesIntroAck,
|
||||
// ),
|
||||
// )))
|
||||
// });
|
||||
// html! {
|
||||
// <WolvesIntro
|
||||
// big_screen={props.big_screen}
|
||||
// on_complete={on_complete}
|
||||
// wolves={wolves.clone()}
|
||||
// />
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Seer {
|
||||
// character_id,
|
||||
// living_players,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: CharacterId| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Seer(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <SingleTarget
|
||||
// targets={living_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"check alignment"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::RoleChange {
|
||||
// character_id,
|
||||
// new_role,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_click = Callback::from(move |_| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::RoleChangeAck),
|
||||
// )))
|
||||
// });
|
||||
// let cont = props.big_screen.not().then(|| {
|
||||
// html! {
|
||||
// <Button on_click={on_click}>{"continue"}</Button>
|
||||
// }
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <h2>{"your role has changed"}</h2>
|
||||
// <p>{new_role.to_string()}</p>
|
||||
// {cont}
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Protector {
|
||||
// character_id,
|
||||
// targets,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: CharacterId| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Protector(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <SingleTarget
|
||||
// targets={targets.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"protector"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Arcanist {
|
||||
// character_id,
|
||||
// living_players,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |(t1, t2): (CharacterId, CharacterId)| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Arcanist(t1, t2)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <TwoTarget
|
||||
// targets={living_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"arcanist"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Gravedigger {
|
||||
// character_id,
|
||||
// dead_players,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: CharacterId| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Gravedigger(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <SingleTarget
|
||||
// targets={dead_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"gravedigger"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Hunter {
|
||||
// character_id,
|
||||
// current_target,
|
||||
// living_players,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: CharacterId| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Hunter(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <SingleTarget
|
||||
// targets={living_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"hunter"}
|
||||
// >
|
||||
// <h3>
|
||||
// <b>{"current target: "}</b>{current_target.clone().map(|t| html!{
|
||||
// <Identity ident={Into::<PublicIdentity>::into(t)} />
|
||||
// }).unwrap_or_else(|| html!{<i>{"none"}</i>})}
|
||||
// </h3>
|
||||
// </SingleTarget>
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Militia {
|
||||
// character_id,
|
||||
// living_players,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: Option<CharacterId>| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Militia(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <OptionalSingleTarget
|
||||
// targets={living_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"pew pew?"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::MapleWolf {
|
||||
// character_id,
|
||||
// kill_or_die,
|
||||
// living_players,
|
||||
// marked,
|
||||
// } => {
|
||||
// let kill_or_die = kill_or_die.then(|| {
|
||||
// html! {
|
||||
// <em>{"if you fail to eat tonight, you will starve"}</em>
|
||||
// }
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <OptionalSingleTarget
|
||||
// targets={living_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"nom nom?"}
|
||||
// >
|
||||
// {kill_or_die}
|
||||
// </OptionalSingleTarget>
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Guardian {
|
||||
// character_id,
|
||||
// previous,
|
||||
// living_players,
|
||||
// marked,
|
||||
// } => {
|
||||
// let last_protect = previous.as_ref().map(|prev| match prev {
|
||||
// PreviousGuardianAction::Protect(target) => {
|
||||
// html! {
|
||||
// <>
|
||||
// <b>{"last night you protected: "}</b>
|
||||
// <Identity ident={Into::<PublicIdentity>::into(target)}/>
|
||||
// </>
|
||||
// }
|
||||
// }
|
||||
// PreviousGuardianAction::Guard(target) => html! {
|
||||
// <>
|
||||
// <b>{"last night you guarded: "}</b>
|
||||
// <Identity ident={Into::<PublicIdentity>::into(target)}/>
|
||||
// </>
|
||||
// },
|
||||
// });
|
||||
// let marked = marked.iter().cloned().collect();
|
||||
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <h2>{"guardian"}</h2>
|
||||
// {last_protect}
|
||||
// <TargetPicker
|
||||
// targets={living_players.clone()}
|
||||
// marked={marked}
|
||||
// mark_callback={mark_callback}
|
||||
// continue_callback={continue_callback}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::WolfPackKill { living_villagers } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
|
||||
// html! {
|
||||
// <SingleTarget
|
||||
// targets={living_villagers.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"wolf pack kill"}
|
||||
// />
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::Shapeshifter { character_id } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then_some({
|
||||
// move |shift| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Shapeshifter(shift)),
|
||||
// )));
|
||||
// }
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <BinaryChoice on_chosen={on_select}>
|
||||
// <h2>{"shapeshift?"}</h2>
|
||||
// </BinaryChoice>
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::AlphaWolf {
|
||||
// character_id,
|
||||
// living_villagers,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: Option<CharacterId>| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::AlphaWolf(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <OptionalSingleTarget
|
||||
// targets={living_villagers.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"alpha wolf target"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// ActionPrompt::DireWolf {
|
||||
// character_id,
|
||||
// living_players,
|
||||
// } => {
|
||||
// let on_complete = props.on_complete.clone();
|
||||
// let on_select = props.big_screen.not().then(|| {
|
||||
// Callback::from(move |target: CharacterId| {
|
||||
// on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||
// HostNightMessage::ActionResponse(ActionResponse::Direwolf(target)),
|
||||
// )));
|
||||
// })
|
||||
// });
|
||||
// html! {
|
||||
// <div>
|
||||
// {identity_html(props, Some(&character_id))}
|
||||
// <SingleTarget
|
||||
// targets={living_players.clone()}
|
||||
// target_selection={on_select}
|
||||
// headline={"direwolf block target"}
|
||||
// />
|
||||
// </div>
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,14 +11,13 @@ use werewolves_proto::{
|
|||
};
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::components::{CoverOfDarkness, Identity};
|
||||
use crate::components::{Button, CoverOfDarkness, Identity};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
pub struct ActionResultProps {
|
||||
pub result: ActionResult,
|
||||
#[prop_or_default]
|
||||
pub ident: Option<PublicIdentity>,
|
||||
#[prop_or_default]
|
||||
pub big_screen: bool,
|
||||
pub on_complete: Callback<HostMessage>,
|
||||
}
|
||||
|
|
@ -40,7 +39,7 @@ pub fn ActionResultView(props: &ActionResultProps) -> Html {
|
|||
let cont = props
|
||||
.big_screen
|
||||
.not()
|
||||
.then(|| html! {<button onclick={on_complete}>{"continue"}</button>});
|
||||
.then(|| html! {<Button on_click={on_complete}>{"continue"}</Button>});
|
||||
match &props.result {
|
||||
ActionResult::RoleBlocked => {
|
||||
html! {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use core::ops::Not;
|
||||
|
||||
use werewolves_proto::{
|
||||
message::{CharacterIdentity, PublicIdentity},
|
||||
role::RoleTitle,
|
||||
|
|
@ -11,14 +9,18 @@ use crate::components::{Button, Identity};
|
|||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
pub struct WolvesIntroProps {
|
||||
pub wolves: Box<[(CharacterIdentity, RoleTitle)]>,
|
||||
pub big_screen: bool,
|
||||
pub on_complete: Callback<()>,
|
||||
pub on_complete: Option<Callback<()>>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn WolvesIntro(props: &WolvesIntroProps) -> Html {
|
||||
let on_complete = props.on_complete.clone();
|
||||
let on_complete = Callback::from(move |_| on_complete.emit(()));
|
||||
let on_complete = props.on_complete.clone().map(|on_complete| {
|
||||
html! {
|
||||
<Button on_click={Callback::from(move |_| on_complete.emit(()))}>
|
||||
{"continue"}
|
||||
</Button>
|
||||
}
|
||||
});
|
||||
html! {
|
||||
<div class="wolves-intro">
|
||||
<h2>{"these are the wolves:"}</h2>
|
||||
|
|
@ -32,11 +34,7 @@ pub fn WolvesIntro(props: &WolvesIntroProps) -> Html {
|
|||
}).collect::<Html>()
|
||||
}
|
||||
</div>
|
||||
{
|
||||
props.big_screen.not().then_some(html!{
|
||||
<Button on_click={on_complete}>{"continue"}</Button>
|
||||
})
|
||||
}
|
||||
{on_complete}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,9 +91,14 @@ pub fn DaytimePlayer(
|
|||
let dead = died_to.is_some().then_some("dead");
|
||||
let marked = on_the_block.then_some("marked");
|
||||
let character_id = identity.character_id.clone();
|
||||
let on_click: Callback<_> = on_select
|
||||
.clone()
|
||||
.map(|on_select| Callback::from(move |_| on_select.emit(character_id.clone())))
|
||||
let on_click: Callback<_> = died_to
|
||||
.is_none()
|
||||
.then_some(())
|
||||
.and(
|
||||
on_select
|
||||
.clone()
|
||||
.map(|on_select| Callback::from(move |_| on_select.emit(character_id.clone()))),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let identity: PublicIdentity = identity.into();
|
||||
html! {
|
||||
|
|
|
|||
Loading…
Reference in New Issue