beholder: now wakes twice in the night
once to pick their target, another time to see what they saw (if any) also fixes host screen (including /big) not responding to some lobby changes
This commit is contained in:
parent
6dd78aa1b5
commit
01c61c143e
|
|
@ -589,11 +589,16 @@ impl Character {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Role::Beholder => prompts.push(ActionPrompt::Beholder {
|
Role::Beholder => {
|
||||||
character_id: self.identity(),
|
prompts.push(ActionPrompt::BeholderChooses {
|
||||||
living_players: village.living_players_excluding(self.character_id()),
|
character_id: self.identity(),
|
||||||
marked: None,
|
living_players: village.living_players_excluding(self.character_id()),
|
||||||
}),
|
marked: None,
|
||||||
|
});
|
||||||
|
prompts.push(ActionPrompt::BeholderWakes {
|
||||||
|
character_id: self.identity(),
|
||||||
|
})
|
||||||
|
}
|
||||||
Role::MasonLeader { .. } => {
|
Role::MasonLeader { .. } => {
|
||||||
log::error!(
|
log::error!(
|
||||||
"night_action_prompts got to MasonLeader, should be handled before the living check"
|
"night_action_prompts got to MasonLeader, should be handled before the living check"
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ pub enum GameError {
|
||||||
NoPreviousDuringDay,
|
NoPreviousDuringDay,
|
||||||
#[error("militia already spent")]
|
#[error("militia already spent")]
|
||||||
MilitiaSpent,
|
MilitiaSpent,
|
||||||
#[error("this role doesn't mark anyone")]
|
#[error("this prompt doesn't mark anyone")]
|
||||||
RoleDoesntMark,
|
PromptDoesntMark,
|
||||||
#[error("cannot shapeshift on a non-shapeshifter prompt")]
|
#[error("cannot shapeshift on a non-shapeshifter prompt")]
|
||||||
ShapeshiftingIsForShapeshifters,
|
ShapeshiftingIsForShapeshifters,
|
||||||
#[error("must select a target")]
|
#[error("must select a target")]
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,9 @@ use crate::{
|
||||||
kill::{self, KillOutcome},
|
kill::{self, KillOutcome},
|
||||||
night::changes::{ChangesLookup, NightChange},
|
night::changes::{ChangesLookup, NightChange},
|
||||||
},
|
},
|
||||||
message::night::{ActionPrompt, ActionPromptTitle, ActionResponse, ActionResult, Visits},
|
message::night::{
|
||||||
|
ActionPrompt, ActionPromptTitle, ActionResponse, ActionResult, ActionType, Visits,
|
||||||
|
},
|
||||||
role::RoleTitle,
|
role::RoleTitle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -62,7 +64,8 @@ impl From<ActionComplete> for ResponseOutcome {
|
||||||
impl ActionPrompt {
|
impl ActionPrompt {
|
||||||
fn unless(&self) -> Option<Unless> {
|
fn unless(&self) -> Option<Unless> {
|
||||||
match &self {
|
match &self {
|
||||||
ActionPrompt::TraitorIntro { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::TraitorIntro { .. }
|
||||||
| ActionPrompt::Insomniac { .. }
|
| ActionPrompt::Insomniac { .. }
|
||||||
| ActionPrompt::MasonsWake { .. }
|
| ActionPrompt::MasonsWake { .. }
|
||||||
| ActionPrompt::WolvesIntro { .. }
|
| ActionPrompt::WolvesIntro { .. }
|
||||||
|
|
@ -124,7 +127,7 @@ impl ActionPrompt {
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| ActionPrompt::Beholder {
|
| ActionPrompt::BeholderChooses {
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +172,7 @@ impl ActionPrompt {
|
||||||
| ActionPrompt::Adjudicator { marked: None, .. }
|
| ActionPrompt::Adjudicator { marked: None, .. }
|
||||||
| ActionPrompt::PowerSeer { marked: None, .. }
|
| ActionPrompt::PowerSeer { marked: None, .. }
|
||||||
| ActionPrompt::Mortician { marked: None, .. }
|
| ActionPrompt::Mortician { marked: None, .. }
|
||||||
| ActionPrompt::Beholder { marked: None, .. }
|
| ActionPrompt::BeholderChooses { marked: None, .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { marked: None, .. }
|
| ActionPrompt::MasonLeaderRecruit { marked: None, .. }
|
||||||
| ActionPrompt::Empath { marked: None, .. }
|
| ActionPrompt::Empath { marked: None, .. }
|
||||||
| ActionPrompt::Vindicator { marked: None, .. }
|
| ActionPrompt::Vindicator { marked: None, .. }
|
||||||
|
|
@ -1003,10 +1006,13 @@ impl Night {
|
||||||
if is_shifted {
|
if is_shifted {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.action_queue
|
self.action_queue.iter().next().map(|n| match n {
|
||||||
.iter()
|
ActionPrompt::BeholderWakes { character_id } => !matches!(
|
||||||
.next()
|
self.get_what_beholder_saw(character_id.character_id),
|
||||||
.map(|n| n.character_id() == Some(curr))
|
None | Some(ActionResult::GoBackToSleep)
|
||||||
|
),
|
||||||
|
_ => n.character_id() == Some(curr),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
|
@ -1175,6 +1181,25 @@ impl Night {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_what_beholder_saw(&self, beholder_id: CharacterId) -> Option<ActionResult> {
|
||||||
|
self.get_actions_for(beholder_id)
|
||||||
|
.into_iter()
|
||||||
|
.find_map(|(p, _)| match p {
|
||||||
|
ActionPrompt::BeholderChooses { marked, .. } => marked,
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.and_then(|beholder_target| {
|
||||||
|
self.died_to_tonight(beholder_target)
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|_| {
|
||||||
|
self.get_actions_for(beholder_target)
|
||||||
|
.into_iter()
|
||||||
|
.find_map(|(prompt, result)| prompt.is_beholdable().then_some(result))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn village(&self) -> &Village {
|
pub const fn village(&self) -> &Village {
|
||||||
&self.village
|
&self.village
|
||||||
}
|
}
|
||||||
|
|
@ -1265,6 +1290,43 @@ impl Night {
|
||||||
ChangesLookup::new(&self.current_changes()).died_to(character_id, self.night, &self.village)
|
ChangesLookup::new(&self.current_changes()).died_to(character_id, self.night, &self.village)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_actions_for(&self, char_id: CharacterId) -> Box<[(ActionPrompt, ActionResult)]> {
|
||||||
|
self.used_actions()
|
||||||
|
.into_iter()
|
||||||
|
.chain(match &self.night_state {
|
||||||
|
NightState::Active {
|
||||||
|
current_prompt,
|
||||||
|
current_result,
|
||||||
|
..
|
||||||
|
} => match current_result {
|
||||||
|
CurrentResult::None => None,
|
||||||
|
CurrentResult::GoBackToSleepAfterShown {
|
||||||
|
result_with_data: action_result,
|
||||||
|
}
|
||||||
|
| CurrentResult::Result(action_result) => {
|
||||||
|
Some((current_prompt.clone(), action_result.clone()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
NightState::Complete => None,
|
||||||
|
})
|
||||||
|
.filter(|(p, _)| {
|
||||||
|
p.character_id()
|
||||||
|
.map(|cid| cid == char_id)
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn beholder_picked(&self) -> Box<[CharacterId]> {
|
||||||
|
self.used_actions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(p, _, _)| match p {
|
||||||
|
ActionPrompt::BeholderChooses { marked, .. } => *marked,
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn currently_dying(&self) -> Box<[DiedTo]> {
|
fn currently_dying(&self) -> Box<[DiedTo]> {
|
||||||
let ch = self.current_changes();
|
let ch = self.current_changes();
|
||||||
let changes: ChangesLookup<'_> = ChangesLookup::new(&ch);
|
let changes: ChangesLookup<'_> = ChangesLookup::new(&ch);
|
||||||
|
|
@ -1390,7 +1452,7 @@ impl Night {
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| ActionPrompt::Beholder {
|
| ActionPrompt::BeholderChooses {
|
||||||
character_id,
|
character_id,
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
|
|
@ -1431,7 +1493,8 @@ impl Night {
|
||||||
..
|
..
|
||||||
} => (*marked == visit_char).then(|| character_id.clone()),
|
} => (*marked == visit_char).then(|| character_id.clone()),
|
||||||
|
|
||||||
ActionPrompt::TraitorIntro { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::TraitorIntro { .. }
|
||||||
| ActionPrompt::Bloodletter { .. }
|
| ActionPrompt::Bloodletter { .. }
|
||||||
| ActionPrompt::WolfPackKill { marked: None, .. }
|
| ActionPrompt::WolfPackKill { marked: None, .. }
|
||||||
| ActionPrompt::Arcanist { marked: _, .. }
|
| ActionPrompt::Arcanist { marked: _, .. }
|
||||||
|
|
@ -1446,7 +1509,7 @@ impl Night {
|
||||||
| ActionPrompt::Adjudicator { marked: None, .. }
|
| ActionPrompt::Adjudicator { marked: None, .. }
|
||||||
| ActionPrompt::PowerSeer { marked: None, .. }
|
| ActionPrompt::PowerSeer { marked: None, .. }
|
||||||
| ActionPrompt::Mortician { marked: None, .. }
|
| ActionPrompt::Mortician { marked: None, .. }
|
||||||
| ActionPrompt::Beholder { marked: None, .. }
|
| ActionPrompt::BeholderChooses { marked: None, .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { marked: None, .. }
|
| ActionPrompt::MasonLeaderRecruit { marked: None, .. }
|
||||||
| ActionPrompt::Empath { marked: None, .. }
|
| ActionPrompt::Empath { marked: None, .. }
|
||||||
| ActionPrompt::Vindicator { marked: None, .. }
|
| ActionPrompt::Vindicator { marked: None, .. }
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,24 @@ impl Night {
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
if let Some(prompt) = self.pull_next_prompt_with_dead_ignore()? {
|
if let Some(prompt) = self.pull_next_prompt_with_dead_ignore()? {
|
||||||
|
if let ActionPrompt::BeholderWakes { character_id } = &prompt
|
||||||
|
&& let Some((target, _)) = self
|
||||||
|
.get_actions_for(character_id.character_id)
|
||||||
|
.into_iter()
|
||||||
|
.find_map(|(prompt, _)| prompt.marked())
|
||||||
|
{
|
||||||
|
let has_beholdable_actions = self
|
||||||
|
.get_actions_for(target)
|
||||||
|
.into_iter()
|
||||||
|
.any(|(p, _)| p.is_beholdable());
|
||||||
|
let died = self.died_to_tonight(target)?.is_some();
|
||||||
|
if !died || !has_beholdable_actions {
|
||||||
|
log::debug!(
|
||||||
|
"skipping beholder wake as their target did not die or wasn't chosen"
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if let ActionPrompt::Insomniac { character_id } = &prompt
|
if let ActionPrompt::Insomniac { character_id } = &prompt
|
||||||
&& self.get_visits_for(character_id.character_id).is_empty()
|
&& self.get_visits_for(character_id.character_id).is_empty()
|
||||||
{
|
{
|
||||||
|
|
@ -237,11 +255,12 @@ impl Night {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pull_next_prompt_with_dead_ignore(&mut self) -> Result<Option<ActionPrompt>> {
|
fn pull_next_prompt_with_dead_ignore(&mut self) -> Result<Option<ActionPrompt>> {
|
||||||
let has_living_beholder = self
|
let beholder_picked = self.beholder_picked();
|
||||||
|
let has_insomniac = self
|
||||||
.village
|
.village
|
||||||
.characters()
|
.characters()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.any(|c| matches!(c.role_title(), RoleTitle::Beholder | RoleTitle::Insomniac));
|
.any(|c| matches!(c.role_title(), RoleTitle::Insomniac));
|
||||||
while let Some(prompt) = self.action_queue.pop_front() {
|
while let Some(prompt) = self.action_queue.pop_front() {
|
||||||
if !matches!(
|
if !matches!(
|
||||||
prompt.action_type(),
|
prompt.action_type(),
|
||||||
|
|
@ -252,7 +271,10 @@ impl Night {
|
||||||
let Some(char_id) = prompt.character_id() else {
|
let Some(char_id) = prompt.character_id() else {
|
||||||
return Ok(Some(prompt));
|
return Ok(Some(prompt));
|
||||||
};
|
};
|
||||||
match (self.died_to_tonight(char_id)?, has_living_beholder) {
|
match (
|
||||||
|
self.died_to_tonight(char_id)?,
|
||||||
|
beholder_picked.contains(&char_id) || has_insomniac,
|
||||||
|
) {
|
||||||
(Some(_), false) => {}
|
(Some(_), false) => {}
|
||||||
(Some(DiedTo::Shapeshift { .. }), _) | (Some(_), true) | (None, _) => {
|
(Some(DiedTo::Shapeshift { .. }), _) | (Some(_), true) | (None, _) => {
|
||||||
return Ok(Some(prompt));
|
return Ok(Some(prompt));
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ use crate::{
|
||||||
game::night::{
|
game::night::{
|
||||||
ActionComplete, CurrentResult, Night, NightState, ResponseOutcome, changes::NightChange,
|
ActionComplete, CurrentResult, Night, NightState, ResponseOutcome, changes::NightChange,
|
||||||
},
|
},
|
||||||
message::night::{ActionPrompt, ActionResponse, ActionResult},
|
message::{
|
||||||
|
CharacterIdentity,
|
||||||
|
night::{ActionPrompt, ActionResponse, ActionResult, ActionType},
|
||||||
|
},
|
||||||
player::Protection,
|
player::Protection,
|
||||||
role::{
|
role::{
|
||||||
Alignment, AlignmentEq, Killer, Powerful, PreviousGuardianAction, RoleBlock, RoleTitle,
|
Alignment, AlignmentEq, Killer, Powerful, PreviousGuardianAction, RoleBlock, RoleTitle,
|
||||||
|
|
@ -116,6 +119,21 @@ impl Night {
|
||||||
};
|
};
|
||||||
|
|
||||||
match current_prompt {
|
match current_prompt {
|
||||||
|
ActionPrompt::BeholderWakes { character_id } => Ok(ActionComplete {
|
||||||
|
result: self
|
||||||
|
.get_what_beholder_saw(character_id.character_id)
|
||||||
|
.map(|saw| {
|
||||||
|
if matches!(saw, ActionResult::RoleBlocked | ActionResult::Drunk) {
|
||||||
|
ActionResult::BeholderSawNothing
|
||||||
|
} else {
|
||||||
|
saw
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(ActionResult::BeholderSawNothing),
|
||||||
|
change: None,
|
||||||
|
secondary_changes: vec![],
|
||||||
|
}
|
||||||
|
.into()),
|
||||||
ActionPrompt::TraitorIntro { .. } => Ok(ActionComplete {
|
ActionPrompt::TraitorIntro { .. } => Ok(ActionComplete {
|
||||||
result: ActionResult::GoBackToSleep,
|
result: ActionResult::GoBackToSleep,
|
||||||
change: None,
|
change: None,
|
||||||
|
|
@ -437,34 +455,14 @@ impl Night {
|
||||||
secondary_changes: vec![],
|
secondary_changes: vec![],
|
||||||
}
|
}
|
||||||
.into()),
|
.into()),
|
||||||
ActionPrompt::Beholder {
|
ActionPrompt::BeholderChooses {
|
||||||
marked: Some(marked),
|
marked: Some(_), ..
|
||||||
..
|
} => Ok(ActionComplete {
|
||||||
} => {
|
result: ActionResult::GoBackToSleep,
|
||||||
if let Some(result) = self.used_actions.iter().find_map(|(prompt, result, _)| {
|
change: None,
|
||||||
prompt.matches_beholding(*marked).then_some(result)
|
secondary_changes: vec![],
|
||||||
}) && self.died_to_tonight(*marked)?.is_some()
|
|
||||||
{
|
|
||||||
Ok(ActionComplete {
|
|
||||||
result: if matches!(result, ActionResult::RoleBlocked | ActionResult::Drunk)
|
|
||||||
{
|
|
||||||
ActionResult::BeholderSawNothing
|
|
||||||
} else {
|
|
||||||
result.clone()
|
|
||||||
},
|
|
||||||
change: None,
|
|
||||||
secondary_changes: vec![],
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
} else {
|
|
||||||
Ok(ActionComplete {
|
|
||||||
result: ActionResult::GoBackToSleep,
|
|
||||||
change: None,
|
|
||||||
secondary_changes: vec![],
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
.into()),
|
||||||
ActionPrompt::MasonsWake { .. } => Ok(ActionComplete {
|
ActionPrompt::MasonsWake { .. } => Ok(ActionComplete {
|
||||||
result: ActionResult::GoBackToSleep,
|
result: ActionResult::GoBackToSleep,
|
||||||
change: None,
|
change: None,
|
||||||
|
|
@ -582,7 +580,7 @@ impl Night {
|
||||||
| ActionPrompt::Adjudicator { marked: None, .. }
|
| ActionPrompt::Adjudicator { marked: None, .. }
|
||||||
| ActionPrompt::PowerSeer { marked: None, .. }
|
| ActionPrompt::PowerSeer { marked: None, .. }
|
||||||
| ActionPrompt::Mortician { marked: None, .. }
|
| ActionPrompt::Mortician { marked: None, .. }
|
||||||
| ActionPrompt::Beholder { marked: None, .. }
|
| ActionPrompt::BeholderChooses { marked: None, .. }
|
||||||
| ActionPrompt::Empath { marked: None, .. }
|
| ActionPrompt::Empath { marked: None, .. }
|
||||||
| ActionPrompt::Vindicator { marked: None, .. }
|
| ActionPrompt::Vindicator { marked: None, .. }
|
||||||
| ActionPrompt::Protector { marked: None, .. }
|
| ActionPrompt::Protector { marked: None, .. }
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,7 @@ pub enum StoryActionPrompt {
|
||||||
impl StoryActionPrompt {
|
impl StoryActionPrompt {
|
||||||
pub fn new(prompt: ActionPrompt) -> Option<Self> {
|
pub fn new(prompt: ActionPrompt) -> Option<Self> {
|
||||||
Some(match prompt {
|
Some(match prompt {
|
||||||
|
ActionPrompt::BeholderWakes { .. } => return None, // TODO: rework story anyway
|
||||||
ActionPrompt::Bloodletter {
|
ActionPrompt::Bloodletter {
|
||||||
character_id,
|
character_id,
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
|
|
@ -317,7 +318,7 @@ impl StoryActionPrompt {
|
||||||
character_id: character_id.character_id,
|
character_id: character_id.character_id,
|
||||||
chosen: marked,
|
chosen: marked,
|
||||||
},
|
},
|
||||||
ActionPrompt::Beholder {
|
ActionPrompt::BeholderChooses {
|
||||||
character_id,
|
character_id,
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
|
|
@ -407,7 +408,7 @@ impl StoryActionPrompt {
|
||||||
| ActionPrompt::Adjudicator { .. }
|
| ActionPrompt::Adjudicator { .. }
|
||||||
| ActionPrompt::PowerSeer { .. }
|
| ActionPrompt::PowerSeer { .. }
|
||||||
| ActionPrompt::Mortician { .. }
|
| ActionPrompt::Mortician { .. }
|
||||||
| ActionPrompt::Beholder { .. }
|
| ActionPrompt::BeholderChooses { .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { .. }
|
| ActionPrompt::MasonLeaderRecruit { .. }
|
||||||
| ActionPrompt::Empath { .. }
|
| ActionPrompt::Empath { .. }
|
||||||
| ActionPrompt::Vindicator { .. }
|
| ActionPrompt::Vindicator { .. }
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,8 @@ pub trait ActionPromptTitleExt {
|
||||||
fn direwolf(&self);
|
fn direwolf(&self);
|
||||||
fn masons_wake(&self);
|
fn masons_wake(&self);
|
||||||
fn masons_leader_recruit(&self);
|
fn masons_leader_recruit(&self);
|
||||||
fn beholder(&self);
|
fn beholder_chooses(&self);
|
||||||
|
fn beholder_wakes(&self);
|
||||||
fn vindicator(&self);
|
fn vindicator(&self);
|
||||||
fn pyremaster(&self);
|
fn pyremaster(&self);
|
||||||
fn empath(&self);
|
fn empath(&self);
|
||||||
|
|
@ -151,8 +152,11 @@ impl ActionPromptTitleExt for ActionPromptTitle {
|
||||||
fn masons_leader_recruit(&self) {
|
fn masons_leader_recruit(&self) {
|
||||||
assert_eq!(*self, ActionPromptTitle::MasonLeaderRecruit)
|
assert_eq!(*self, ActionPromptTitle::MasonLeaderRecruit)
|
||||||
}
|
}
|
||||||
fn beholder(&self) {
|
fn beholder_chooses(&self) {
|
||||||
assert_eq!(*self, ActionPromptTitle::Beholder)
|
assert_eq!(*self, ActionPromptTitle::BeholderChooses)
|
||||||
|
}
|
||||||
|
fn beholder_wakes(&self) {
|
||||||
|
assert_eq!(*self, ActionPromptTitle::BeholderWakes)
|
||||||
}
|
}
|
||||||
fn vindicator(&self) {
|
fn vindicator(&self) {
|
||||||
assert_eq!(*self, ActionPromptTitle::Vindicator)
|
assert_eq!(*self, ActionPromptTitle::Vindicator)
|
||||||
|
|
@ -414,7 +418,8 @@ impl GameExt for Game {
|
||||||
fn mark_and_check(&mut self, mark: CharacterId) {
|
fn mark_and_check(&mut self, mark: CharacterId) {
|
||||||
let prompt = self.mark(mark);
|
let prompt = self.mark(mark);
|
||||||
match prompt {
|
match prompt {
|
||||||
ActionPrompt::TraitorIntro { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::TraitorIntro { .. }
|
||||||
| ActionPrompt::Insomniac { .. }
|
| ActionPrompt::Insomniac { .. }
|
||||||
| ActionPrompt::MasonsWake { .. }
|
| ActionPrompt::MasonsWake { .. }
|
||||||
| ActionPrompt::ElderReveal { .. }
|
| ActionPrompt::ElderReveal { .. }
|
||||||
|
|
@ -448,7 +453,7 @@ impl GameExt for Game {
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| ActionPrompt::Beholder {
|
| ActionPrompt::BeholderChooses {
|
||||||
marked: Some(marked),
|
marked: Some(marked),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
|
@ -509,7 +514,7 @@ impl GameExt for Game {
|
||||||
| ActionPrompt::Adjudicator { marked: None, .. }
|
| ActionPrompt::Adjudicator { marked: None, .. }
|
||||||
| ActionPrompt::PowerSeer { marked: None, .. }
|
| ActionPrompt::PowerSeer { marked: None, .. }
|
||||||
| ActionPrompt::Mortician { marked: None, .. }
|
| ActionPrompt::Mortician { marked: None, .. }
|
||||||
| ActionPrompt::Beholder { marked: None, .. }
|
| ActionPrompt::BeholderChooses { marked: None, .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { marked: None, .. }
|
| ActionPrompt::MasonLeaderRecruit { marked: None, .. }
|
||||||
| ActionPrompt::Empath { marked: None, .. }
|
| ActionPrompt::Empath { marked: None, .. }
|
||||||
| ActionPrompt::Vindicator { marked: None, .. }
|
| ActionPrompt::Vindicator { marked: None, .. }
|
||||||
|
|
@ -960,6 +965,10 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.mark(game.character_by_player_id(insomniac).character_id());
|
game.mark(game.character_by_player_id(insomniac).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(power_seer).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(werewolf).character_id());
|
game.mark(game.character_by_player_id(werewolf).character_id());
|
||||||
game.r#continue().seer();
|
game.r#continue().seer();
|
||||||
|
|
@ -1000,10 +1009,6 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.r#continue().insomniac();
|
game.r#continue().insomniac();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark(game.character_by_player_id(power_seer).character_id());
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -1040,6 +1045,10 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.mark(game.character_by_player_id(insomniac).character_id());
|
game.mark(game.character_by_player_id(insomniac).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(power_seer).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(werewolf).character_id());
|
game.mark(game.character_by_player_id(werewolf).character_id());
|
||||||
game.r#continue().seer();
|
game.r#continue().seer();
|
||||||
|
|
@ -1080,10 +1089,6 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.r#continue().insomniac();
|
game.r#continue().insomniac();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark(game.character_by_player_id(power_seer).character_id());
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
game.mark_for_execution(
|
game.mark_for_execution(
|
||||||
game.living_villager_excl(protect.player_id())
|
game.living_villager_excl(protect.player_id())
|
||||||
|
|
@ -1121,6 +1126,10 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.mark(game.character_by_player_id(insomniac).character_id());
|
game.mark(game.character_by_player_id(insomniac).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(gravedigger).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(shapeshifter).character_id());
|
game.mark(game.character_by_player_id(shapeshifter).character_id());
|
||||||
game.r#continue().seer();
|
game.r#continue().seer();
|
||||||
|
|
@ -1156,10 +1165,6 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.r#continue().insomniac();
|
game.r#continue().insomniac();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark(game.character_by_player_id(gravedigger).character_id());
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
game.mark_for_execution(game.character_by_player_id(vindicator).character_id());
|
game.mark_for_execution(game.character_by_player_id(vindicator).character_id());
|
||||||
game.execute().title().wolf_pack_kill();
|
game.execute().title().wolf_pack_kill();
|
||||||
|
|
@ -1174,6 +1179,10 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.mark(game.character_by_player_id(empath).character_id());
|
game.mark(game.character_by_player_id(empath).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(mortician).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(insomniac).character_id());
|
game.mark(game.character_by_player_id(insomniac).character_id());
|
||||||
game.r#continue().seer();
|
game.r#continue().seer();
|
||||||
|
|
@ -1212,8 +1221,7 @@ fn big_game_test_based_on_story_test() {
|
||||||
game.r#continue().insomniac();
|
game.r#continue().insomniac();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
game.next().title().beholder_wakes();
|
||||||
game.mark(game.character_by_player_id(mortician).character_id());
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
game.r#continue().mortician(),
|
game.r#continue().mortician(),
|
||||||
DiedToTitle::GuardianProtecting
|
DiedToTitle::GuardianProtecting
|
||||||
|
|
|
||||||
|
|
@ -372,6 +372,10 @@ fn previous_prompt() {
|
||||||
game.mark(game.character_by_player_id(insomniac).character_id());
|
game.mark(game.character_by_player_id(insomniac).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(power_seer).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(werewolf).character_id());
|
game.mark(game.character_by_player_id(werewolf).character_id());
|
||||||
game.r#continue().seer();
|
game.r#continue().seer();
|
||||||
|
|
@ -412,9 +416,5 @@ fn previous_prompt() {
|
||||||
game.r#continue().insomniac();
|
game.r#continue().insomniac();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark(game.character_by_player_id(power_seer).character_id());
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ fn beholder_appropriate_prompt_position() {
|
||||||
game.mark(game.character_by_player_id(beholder).character_id());
|
game.mark(game.character_by_player_id(beholder).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
game.next().title().beholder_chooses();
|
||||||
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
|
@ -67,5 +67,5 @@ fn beholder_appropriate_prompt_position() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
game.r#continue().r#continue();
|
game.r#continue().r#continue();
|
||||||
game.next().title().beholder();
|
game.next().title().beholder_chooses();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,33 +51,85 @@ fn beholding_seer() {
|
||||||
game.mark(game.living_villager().character_id());
|
game.mark(game.living_villager().character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(seer_player_id).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
||||||
game.r#continue().seer().wolves();
|
game.r#continue().seer().wolves();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark(game.character_by_player_id(seer_player_id).character_id());
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
game.execute().title().wolf_pack_kill();
|
game.execute().title().wolf_pack_kill();
|
||||||
game.mark(game.character_by_player_id(seer_player_id).character_id());
|
game.mark(game.character_by_player_id(seer_player_id).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(seer_player_id).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().seer();
|
game.next().title().seer();
|
||||||
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
||||||
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
game.next().title().beholder_wakes();
|
||||||
game.mark(game.character_by_player_id(seer_player_id).character_id());
|
|
||||||
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn beholding_seer_but_arcanist_dies__arcanist_shouldnt_wake() {
|
||||||
|
init_log();
|
||||||
|
let players = gen_players(1..21);
|
||||||
|
let mut player_ids = players.iter().map(|p| p.player_id);
|
||||||
|
let seer = player_ids.next().unwrap();
|
||||||
|
let wolf = player_ids.next().unwrap();
|
||||||
|
let beholder = player_ids.next().unwrap();
|
||||||
|
let arcanist = player_ids.next().unwrap();
|
||||||
|
let mut settings = GameSettings::empty();
|
||||||
|
settings.add_and_assign(SetupRole::Seer, seer);
|
||||||
|
settings.add_and_assign(SetupRole::Werewolf, wolf);
|
||||||
|
settings.add_and_assign(SetupRole::Beholder, beholder);
|
||||||
|
settings.add_and_assign(SetupRole::Arcanist, arcanist);
|
||||||
|
settings.fill_remaining_slots_with_villagers(players.len());
|
||||||
|
let mut game = Game::new(&players, settings).unwrap();
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().seer();
|
||||||
|
game.mark(game.character_by_player_id(wolf).character_id());
|
||||||
|
game.r#continue().seer().wolves();
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().arcanist();
|
||||||
|
game.mark(game.character_by_player_id(seer).character_id());
|
||||||
|
game.mark_villager();
|
||||||
|
game.r#continue().arcanist();
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
game.execute().title().wolf_pack_kill();
|
||||||
|
game.mark(game.character_by_player_id(arcanist).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(game.character_by_player_id(seer).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().seer();
|
||||||
|
game.mark(game.character_by_player_id(wolf).character_id());
|
||||||
|
game.r#continue().seer().wolves();
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn beholding_wolf() {
|
fn beholding_wolf() {
|
||||||
let players = gen_players(1..10);
|
let players = gen_players(1..10);
|
||||||
|
|
@ -97,9 +149,36 @@ fn beholding_wolf() {
|
||||||
game.mark(game.living_villager_excl(beholder_player_id).character_id());
|
game.mark(game.living_villager_excl(beholder_player_id).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
game.next().title().beholder_chooses();
|
||||||
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn beholding_villager() {
|
||||||
|
let players = gen_players(1..10);
|
||||||
|
let wolf_player_id = players[1].player_id;
|
||||||
|
let beholder_player_id = players[2].player_id;
|
||||||
|
let mut settings = GameSettings::empty();
|
||||||
|
settings.add_and_assign(SetupRole::Werewolf, wolf_player_id);
|
||||||
|
settings.add_and_assign(SetupRole::Beholder, beholder_player_id);
|
||||||
|
settings.fill_remaining_slots_with_villagers(9);
|
||||||
|
let mut game = Game::new(&players, settings).unwrap();
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro);
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
game.execute().title().wolf_pack_kill();
|
||||||
|
let target = game.living_villager();
|
||||||
|
game.mark(target.character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark(target.character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ use crate::{
|
||||||
diedto::DiedTo,
|
diedto::DiedTo,
|
||||||
game::{Game, GameSettings, OrRandom, SetupRole},
|
game::{Game, GameSettings, OrRandom, SetupRole},
|
||||||
game_test::{ActionPromptTitleExt, ActionResultExt, GameExt, SettingsExt, gen_players},
|
game_test::{ActionPromptTitleExt, ActionResultExt, GameExt, SettingsExt, gen_players},
|
||||||
message::night::{ActionPrompt, ActionPromptTitle},
|
message::night::{ActionPrompt, ActionPromptTitle, ActionResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -278,6 +278,10 @@ fn masons_get_go_back_to_sleep() {
|
||||||
game.mark_villager();
|
game.mark_villager();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark_villager();
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().masons_leader_recruit();
|
game.next().title().masons_leader_recruit();
|
||||||
game.mark(game.character_by_player_id(scapegoat).character_id());
|
game.mark(game.character_by_player_id(scapegoat).character_id());
|
||||||
game.r#continue().r#continue();
|
game.r#continue().r#continue();
|
||||||
|
|
@ -285,21 +289,17 @@ fn masons_get_go_back_to_sleep() {
|
||||||
game.next().title().masons_wake();
|
game.next().title().masons_wake();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
game.next_expect_day();
|
||||||
|
game.execute().title().wolf_pack_kill();
|
||||||
game.mark_villager();
|
game.mark_villager();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next().title().beholder_chooses();
|
||||||
game.execute().title().wolf_pack_kill();
|
|
||||||
game.mark_villager();
|
game.mark_villager();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().masons_wake();
|
game.next().title().masons_wake();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark_villager();
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ fn shapeshift_removes_village_prompt_but_previous_can_bring_it_back() {
|
||||||
);
|
);
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
game.next().title().beholder_chooses();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
game.prev(),
|
game.prev(),
|
||||||
|
|
@ -411,6 +411,18 @@ fn shapeshift_removes_village_prompt_but_previous_can_bring_it_back() {
|
||||||
ChangesLookup::new(¤t_changes).shapeshift_change(),
|
ChangesLookup::new(¤t_changes).shapeshift_change(),
|
||||||
None
|
None
|
||||||
);
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
game.prev(),
|
||||||
|
ServerToHostMessage::ActionPrompt(ActionPrompt::WolfPackKill { .. }, _)
|
||||||
|
));
|
||||||
|
game.mark_villager();
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
|
||||||
|
game.next().title().shapeshifter();
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder_chooses();
|
||||||
|
game.mark_villager();
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().gravedigger();
|
game.next().title().gravedigger();
|
||||||
|
|
@ -418,10 +430,6 @@ fn shapeshift_removes_village_prompt_but_previous_can_bring_it_back() {
|
||||||
assert_eq!(game.r#continue().gravedigger(), Some(RoleTitle::Villager));
|
assert_eq!(game.r#continue().gravedigger(), Some(RoleTitle::Villager));
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
game.next().title().beholder();
|
|
||||||
game.mark_villager();
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
game.character_by_player_id(gravedigger).role_title(),
|
game.character_by_player_id(gravedigger).role_title(),
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ pub enum HostLobbyMessage {
|
||||||
ManufacturePlayer(PublicIdentity),
|
ManufacturePlayer(PublicIdentity),
|
||||||
Kick(PlayerId),
|
Kick(PlayerId),
|
||||||
SetPlayerNumber(PlayerId, NonZeroU8),
|
SetPlayerNumber(PlayerId, NonZeroU8),
|
||||||
GetGameSettings,
|
|
||||||
SetGameSettings(GameSettings),
|
SetGameSettings(GameSettings),
|
||||||
SetQrMode(bool),
|
SetQrMode(bool),
|
||||||
Start,
|
Start,
|
||||||
|
|
@ -98,9 +97,11 @@ pub enum ServerToHostMessage {
|
||||||
PlayerStates(Box<[CharacterState]>),
|
PlayerStates(Box<[CharacterState]>),
|
||||||
ActionPrompt(ActionPrompt, usize),
|
ActionPrompt(ActionPrompt, usize),
|
||||||
ActionResult(Option<CharacterIdentity>, ActionResult),
|
ActionResult(Option<CharacterIdentity>, ActionResult),
|
||||||
Lobby(Box<[PlayerState]>),
|
Lobby {
|
||||||
|
players: Box<[PlayerState]>,
|
||||||
|
settings: GameSettings,
|
||||||
|
},
|
||||||
QrMode(bool),
|
QrMode(bool),
|
||||||
GameSettings(GameSettings),
|
|
||||||
Error(GameError),
|
Error(GameError),
|
||||||
GameOver(GameOver),
|
GameOver(GameOver),
|
||||||
WaitingForRoleRevealAcks {
|
WaitingForRoleRevealAcks {
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ pub enum ActionType {
|
||||||
LoneWolfKill,
|
LoneWolfKill,
|
||||||
Block,
|
Block,
|
||||||
VillageKill,
|
VillageKill,
|
||||||
|
BeholderChooses,
|
||||||
Intel,
|
Intel,
|
||||||
Other,
|
|
||||||
MasonRecruit,
|
MasonRecruit,
|
||||||
MasonsWake,
|
MasonsWake,
|
||||||
Insomniac,
|
Insomniac,
|
||||||
Beholder,
|
BeholderWakes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ChecksAs, Titles, Extract)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ChecksAs, Titles, Extract)]
|
||||||
|
|
@ -140,8 +140,8 @@ pub enum ActionPrompt {
|
||||||
dead_players: Box<[CharacterIdentity]>,
|
dead_players: Box<[CharacterIdentity]>,
|
||||||
marked: Option<CharacterId>,
|
marked: Option<CharacterId>,
|
||||||
},
|
},
|
||||||
#[checks(ActionType::Beholder)]
|
#[checks(ActionType::BeholderChooses)]
|
||||||
Beholder {
|
BeholderChooses {
|
||||||
character_id: CharacterIdentity,
|
character_id: CharacterIdentity,
|
||||||
living_players: Box<[CharacterIdentity]>,
|
living_players: Box<[CharacterIdentity]>,
|
||||||
marked: Option<CharacterId>,
|
marked: Option<CharacterId>,
|
||||||
|
|
@ -212,9 +212,46 @@ pub enum ActionPrompt {
|
||||||
},
|
},
|
||||||
#[checks(ActionType::TraitorIntro)]
|
#[checks(ActionType::TraitorIntro)]
|
||||||
TraitorIntro { character_id: CharacterIdentity },
|
TraitorIntro { character_id: CharacterIdentity },
|
||||||
|
#[checks(ActionType::BeholderWakes)]
|
||||||
|
BeholderWakes { character_id: CharacterIdentity },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActionPrompt {
|
impl ActionPrompt {
|
||||||
|
pub const fn is_beholdable(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ActionPrompt::BeholderChooses { .. }
|
||||||
|
| ActionPrompt::RoleChange { .. }
|
||||||
|
| ActionPrompt::ElderReveal { .. }
|
||||||
|
| ActionPrompt::Protector { .. }
|
||||||
|
| ActionPrompt::Hunter { .. }
|
||||||
|
| ActionPrompt::Militia { .. }
|
||||||
|
| ActionPrompt::MapleWolf { .. }
|
||||||
|
| ActionPrompt::Guardian { .. }
|
||||||
|
| ActionPrompt::WolvesIntro { .. }
|
||||||
|
| ActionPrompt::MasonsWake { .. }
|
||||||
|
| ActionPrompt::MasonLeaderRecruit { .. }
|
||||||
|
| ActionPrompt::Vindicator { .. }
|
||||||
|
| ActionPrompt::PyreMaster { .. }
|
||||||
|
| ActionPrompt::WolfPackKill { .. }
|
||||||
|
| ActionPrompt::Shapeshifter { .. }
|
||||||
|
| ActionPrompt::AlphaWolf { .. }
|
||||||
|
| ActionPrompt::DireWolf { .. }
|
||||||
|
| ActionPrompt::LoneWolfKill { .. }
|
||||||
|
| ActionPrompt::Bloodletter { .. }
|
||||||
|
| ActionPrompt::TraitorIntro { .. }
|
||||||
|
| ActionPrompt::CoverOfDarkness => false,
|
||||||
|
|
||||||
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::Seer { .. }
|
||||||
|
| ActionPrompt::Arcanist { .. }
|
||||||
|
| ActionPrompt::Gravedigger { .. }
|
||||||
|
| ActionPrompt::Adjudicator { .. }
|
||||||
|
| ActionPrompt::PowerSeer { .. }
|
||||||
|
| ActionPrompt::Mortician { .. }
|
||||||
|
| ActionPrompt::Empath { .. }
|
||||||
|
| ActionPrompt::Insomniac { .. } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub(crate) const fn marked(&self) -> Option<(CharacterId, Option<CharacterId>)> {
|
pub(crate) const fn marked(&self) -> Option<(CharacterId, Option<CharacterId>)> {
|
||||||
match self {
|
match self {
|
||||||
ActionPrompt::Seer { marked, .. }
|
ActionPrompt::Seer { marked, .. }
|
||||||
|
|
@ -227,7 +264,7 @@ impl ActionPrompt {
|
||||||
| ActionPrompt::Adjudicator { marked, .. }
|
| ActionPrompt::Adjudicator { marked, .. }
|
||||||
| ActionPrompt::PowerSeer { marked, .. }
|
| ActionPrompt::PowerSeer { marked, .. }
|
||||||
| ActionPrompt::Mortician { marked, .. }
|
| ActionPrompt::Mortician { marked, .. }
|
||||||
| ActionPrompt::Beholder { marked, .. }
|
| ActionPrompt::BeholderChooses { marked, .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { marked, .. }
|
| ActionPrompt::MasonLeaderRecruit { marked, .. }
|
||||||
| ActionPrompt::Empath { marked, .. }
|
| ActionPrompt::Empath { marked, .. }
|
||||||
| ActionPrompt::Vindicator { marked, .. }
|
| ActionPrompt::Vindicator { marked, .. }
|
||||||
|
|
@ -257,6 +294,7 @@ impl ActionPrompt {
|
||||||
marked: (None, None),
|
marked: (None, None),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
|
| ActionPrompt::BeholderWakes { .. }
|
||||||
| ActionPrompt::CoverOfDarkness
|
| ActionPrompt::CoverOfDarkness
|
||||||
| ActionPrompt::WolvesIntro { .. }
|
| ActionPrompt::WolvesIntro { .. }
|
||||||
| ActionPrompt::RoleChange { .. }
|
| ActionPrompt::RoleChange { .. }
|
||||||
|
|
@ -269,7 +307,8 @@ impl ActionPrompt {
|
||||||
}
|
}
|
||||||
pub(crate) const fn character_id(&self) -> Option<CharacterId> {
|
pub(crate) const fn character_id(&self) -> Option<CharacterId> {
|
||||||
match self {
|
match self {
|
||||||
ActionPrompt::TraitorIntro { character_id }
|
ActionPrompt::BeholderWakes { character_id }
|
||||||
|
| ActionPrompt::TraitorIntro { character_id }
|
||||||
| ActionPrompt::Insomniac { character_id, .. }
|
| ActionPrompt::Insomniac { character_id, .. }
|
||||||
| ActionPrompt::LoneWolfKill { character_id, .. }
|
| ActionPrompt::LoneWolfKill { character_id, .. }
|
||||||
| ActionPrompt::ElderReveal { character_id }
|
| ActionPrompt::ElderReveal { character_id }
|
||||||
|
|
@ -287,7 +326,7 @@ impl ActionPrompt {
|
||||||
| ActionPrompt::Adjudicator { character_id, .. }
|
| ActionPrompt::Adjudicator { character_id, .. }
|
||||||
| ActionPrompt::PowerSeer { character_id, .. }
|
| ActionPrompt::PowerSeer { character_id, .. }
|
||||||
| ActionPrompt::Mortician { character_id, .. }
|
| ActionPrompt::Mortician { character_id, .. }
|
||||||
| ActionPrompt::Beholder { character_id, .. }
|
| ActionPrompt::BeholderChooses { character_id, .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { character_id, .. }
|
| ActionPrompt::MasonLeaderRecruit { character_id, .. }
|
||||||
| ActionPrompt::Empath { character_id, .. }
|
| ActionPrompt::Empath { character_id, .. }
|
||||||
| ActionPrompt::Vindicator { character_id, .. }
|
| ActionPrompt::Vindicator { character_id, .. }
|
||||||
|
|
@ -313,8 +352,9 @@ impl ActionPrompt {
|
||||||
| ActionPrompt::Mortician { character_id, .. }
|
| ActionPrompt::Mortician { character_id, .. }
|
||||||
| ActionPrompt::Vindicator { character_id, .. } => character_id.character_id == target,
|
| ActionPrompt::Vindicator { character_id, .. } => character_id.character_id == target,
|
||||||
|
|
||||||
ActionPrompt::TraitorIntro { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
| ActionPrompt::Beholder { .. }
|
| ActionPrompt::TraitorIntro { .. }
|
||||||
|
| ActionPrompt::BeholderChooses { .. }
|
||||||
| ActionPrompt::CoverOfDarkness
|
| ActionPrompt::CoverOfDarkness
|
||||||
| ActionPrompt::WolvesIntro { .. }
|
| ActionPrompt::WolvesIntro { .. }
|
||||||
| ActionPrompt::RoleChange { .. }
|
| ActionPrompt::RoleChange { .. }
|
||||||
|
|
@ -341,7 +381,7 @@ impl ActionPrompt {
|
||||||
Self::Shapeshifter { .. } => true,
|
Self::Shapeshifter { .. } => true,
|
||||||
_ => !matches!(
|
_ => !matches!(
|
||||||
self.with_mark(CharacterId::new()),
|
self.with_mark(CharacterId::new()),
|
||||||
Err(GameError::RoleDoesntMark)
|
Err(GameError::PromptDoesntMark)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -349,14 +389,15 @@ impl ActionPrompt {
|
||||||
pub(crate) fn with_mark(&self, mark: CharacterId) -> Result<ActionPrompt> {
|
pub(crate) fn with_mark(&self, mark: CharacterId) -> Result<ActionPrompt> {
|
||||||
let mut prompt = self.clone();
|
let mut prompt = self.clone();
|
||||||
match &mut prompt {
|
match &mut prompt {
|
||||||
ActionPrompt::TraitorIntro { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::TraitorIntro { .. }
|
||||||
| ActionPrompt::Insomniac { .. }
|
| ActionPrompt::Insomniac { .. }
|
||||||
| ActionPrompt::MasonsWake { .. }
|
| ActionPrompt::MasonsWake { .. }
|
||||||
| ActionPrompt::ElderReveal { .. }
|
| ActionPrompt::ElderReveal { .. }
|
||||||
| ActionPrompt::WolvesIntro { .. }
|
| ActionPrompt::WolvesIntro { .. }
|
||||||
| ActionPrompt::RoleChange { .. }
|
| ActionPrompt::RoleChange { .. }
|
||||||
| ActionPrompt::Shapeshifter { .. }
|
| ActionPrompt::Shapeshifter { .. }
|
||||||
| ActionPrompt::CoverOfDarkness => Err(GameError::RoleDoesntMark),
|
| ActionPrompt::CoverOfDarkness => Err(GameError::PromptDoesntMark),
|
||||||
|
|
||||||
ActionPrompt::Guardian {
|
ActionPrompt::Guardian {
|
||||||
previous,
|
previous,
|
||||||
|
|
@ -446,7 +487,7 @@ impl ActionPrompt {
|
||||||
marked,
|
marked,
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| ActionPrompt::Beholder {
|
| ActionPrompt::BeholderChooses {
|
||||||
living_players: targets,
|
living_players: targets,
|
||||||
marked,
|
marked,
|
||||||
..
|
..
|
||||||
|
|
|
||||||
|
|
@ -423,11 +423,13 @@ impl GameEnd {
|
||||||
return self.process(Message::Host(HostMessage::GetState));
|
return self.process(Message::Host(HostMessage::GetState));
|
||||||
}
|
}
|
||||||
Message::Host(HostMessage::PostGame(PostGameMessage::NewLobby)) => {
|
Message::Host(HostMessage::PostGame(PostGameMessage::NewLobby)) => {
|
||||||
self.game()
|
let game = self.game().ok()?;
|
||||||
.ok()?
|
game.comms
|
||||||
.comms
|
|
||||||
.host()
|
.host()
|
||||||
.send(ServerToHostMessage::Lobby(Box::new([])))
|
.send(ServerToHostMessage::Lobby {
|
||||||
|
players: Box::new([]),
|
||||||
|
settings: game.game.village().settings(),
|
||||||
|
})
|
||||||
.log_debug();
|
.log_debug();
|
||||||
let lobby = self.game.take()?.into_lobby();
|
let lobby = self.game.take()?.into_lobby();
|
||||||
return Some(ProcessOutcome::Lobby(lobby));
|
return Some(ProcessOutcome::Lobby(lobby));
|
||||||
|
|
|
||||||
|
|
@ -57,12 +57,6 @@ impl Lobby {
|
||||||
|
|
||||||
pub fn set_settings(&mut self, settings: GameSettings) {
|
pub fn set_settings(&mut self, settings: GameSettings) {
|
||||||
self.settings = settings.clone();
|
self.settings = settings.clone();
|
||||||
if let Ok(comms) = self.comms() {
|
|
||||||
comms
|
|
||||||
.host()
|
|
||||||
.send(ServerToHostMessage::GameSettings(settings))
|
|
||||||
.log_debug();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_players_in_lobby(&mut self, players_in_lobby: LobbyPlayers) {
|
pub fn set_players_in_lobby(&mut self, players_in_lobby: LobbyPlayers) {
|
||||||
|
|
@ -94,7 +88,7 @@ impl Lobby {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_lobby_player_list(&self) -> Box<[PlayerState]> {
|
async fn get_lobby_info(&self) -> Box<[PlayerState]> {
|
||||||
let mut players = Vec::new();
|
let mut players = Vec::new();
|
||||||
for (player, _) in self.players_in_lobby.iter() {
|
for (player, _) in self.players_in_lobby.iter() {
|
||||||
players.push(PlayerState {
|
players.push(PlayerState {
|
||||||
|
|
@ -107,10 +101,11 @@ impl Lobby {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_lobby_info_to_host(&mut self) -> Result<(), GameError> {
|
pub async fn send_lobby_info_to_host(&mut self) -> Result<(), GameError> {
|
||||||
let players = self.get_lobby_player_list().await;
|
let players = self.get_lobby_info().await;
|
||||||
let qr_mode = self.qr_mode;
|
let qr_mode = self.qr_mode;
|
||||||
|
let settings = self.settings.clone();
|
||||||
let host = self.comms()?.host();
|
let host = self.comms()?.host();
|
||||||
host.send(ServerToHostMessage::Lobby(players))?;
|
host.send(ServerToHostMessage::Lobby { players, settings })?;
|
||||||
host.send(ServerToHostMessage::QrMode(qr_mode))
|
host.send(ServerToHostMessage::QrMode(qr_mode))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,18 +207,10 @@ impl Lobby {
|
||||||
Message::Host(HostMessage::Lobby(HostLobbyMessage::GetState))
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::GetState))
|
||||||
| Message::Host(HostMessage::GetState) => {
|
| Message::Host(HostMessage::GetState) => {
|
||||||
self.send_lobby_info_to_host().await?;
|
self.send_lobby_info_to_host().await?;
|
||||||
let settings = self.settings.clone();
|
|
||||||
self.comms()?
|
|
||||||
.host()
|
|
||||||
.send(ServerToHostMessage::GameSettings(settings))
|
|
||||||
.log_warn();
|
|
||||||
}
|
|
||||||
Message::Host(HostMessage::Lobby(HostLobbyMessage::GetGameSettings)) => {
|
|
||||||
let msg = ServerToHostMessage::GameSettings(self.settings.clone());
|
|
||||||
let _ = self.comms().unwrap().host().send(msg);
|
|
||||||
}
|
}
|
||||||
Message::Host(HostMessage::Lobby(HostLobbyMessage::SetGameSettings(settings))) => {
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::SetGameSettings(settings))) => {
|
||||||
self.settings = settings;
|
self.settings = settings;
|
||||||
|
self.send_lobby_info_to_host().await?;
|
||||||
}
|
}
|
||||||
Message::Host(HostMessage::Lobby(HostLobbyMessage::SetPlayerNumber(pid, num))) => {
|
Message::Host(HostMessage::Lobby(HostLobbyMessage::SetPlayerNumber(pid, num))) => {
|
||||||
self.joined_players
|
self.joined_players
|
||||||
|
|
|
||||||
|
|
@ -194,9 +194,11 @@ pub enum HostEvent {
|
||||||
SetBigScreenState(bool),
|
SetBigScreenState(bool),
|
||||||
SetState(HostState),
|
SetState(HostState),
|
||||||
Continue,
|
Continue,
|
||||||
PlayerList(Box<[PlayerState]>),
|
Lobby {
|
||||||
|
players: Box<[PlayerState]>,
|
||||||
|
settings: GameSettings,
|
||||||
|
},
|
||||||
CharacterList(Box<[CharacterState]>),
|
CharacterList(Box<[CharacterState]>),
|
||||||
Settings(GameSettings),
|
|
||||||
Error(GameError),
|
Error(GameError),
|
||||||
QrMode(bool),
|
QrMode(bool),
|
||||||
ToOverrideView,
|
ToOverrideView,
|
||||||
|
|
@ -253,8 +255,9 @@ impl From<ServerToHostMessage> for HostEvent {
|
||||||
marked_for_execution,
|
marked_for_execution,
|
||||||
settings,
|
settings,
|
||||||
}),
|
}),
|
||||||
ServerToHostMessage::Lobby(players) => HostEvent::PlayerList(players),
|
ServerToHostMessage::Lobby { players, settings } => {
|
||||||
ServerToHostMessage::GameSettings(settings) => HostEvent::Settings(settings),
|
HostEvent::Lobby { players, settings }
|
||||||
|
}
|
||||||
ServerToHostMessage::Error(err) => HostEvent::Error(err),
|
ServerToHostMessage::Error(err) => HostEvent::Error(err),
|
||||||
ServerToHostMessage::GameOver(game_over) => {
|
ServerToHostMessage::GameOver(game_over) => {
|
||||||
HostEvent::SetState(HostState::GameOver { result: game_over })
|
HostEvent::SetState(HostState::GameOver { result: game_over })
|
||||||
|
|
@ -695,7 +698,10 @@ impl Host {
|
||||||
(Some(HostState::CharacterStates(char)), true)
|
(Some(HostState::CharacterStates(char)), true)
|
||||||
}
|
}
|
||||||
HostEvent::QrMode(mode) => (None, true),
|
HostEvent::QrMode(mode) => (None, true),
|
||||||
HostEvent::PlayerList(mut players) => {
|
HostEvent::Lobby {
|
||||||
|
mut players,
|
||||||
|
settings,
|
||||||
|
} => {
|
||||||
const LAST: NonZeroU8 = NonZeroU8::new(0xFF).unwrap();
|
const LAST: NonZeroU8 = NonZeroU8::new(0xFF).unwrap();
|
||||||
players.sort_by(|l, r| {
|
players.sort_by(|l, r| {
|
||||||
l.identification
|
l.identification
|
||||||
|
|
@ -704,69 +710,16 @@ impl Host {
|
||||||
.unwrap_or(LAST)
|
.unwrap_or(LAST)
|
||||||
.cmp(&r.identification.public.number.unwrap_or(LAST))
|
.cmp(&r.identification.public.number.unwrap_or(LAST))
|
||||||
});
|
});
|
||||||
match &self.state {
|
(
|
||||||
HostState::Lobby { settings, .. } => (
|
Some(HostState::Lobby {
|
||||||
Some(HostState::Lobby {
|
settings,
|
||||||
players: players.into_iter().collect(),
|
players: players.into_iter().collect(),
|
||||||
settings: settings.clone(),
|
}),
|
||||||
}),
|
true,
|
||||||
true,
|
)
|
||||||
),
|
|
||||||
HostState::Story { .. }
|
|
||||||
| HostState::Disconnected
|
|
||||||
| HostState::GameOver { .. } => {
|
|
||||||
let mut send = self.send.clone();
|
|
||||||
let on_err = self.error_callback.clone();
|
|
||||||
|
|
||||||
let state = Some(HostState::Lobby {
|
|
||||||
players: players.into_iter().collect(),
|
|
||||||
settings: Default::default(),
|
|
||||||
});
|
|
||||||
yew::platform::spawn_local(async move {
|
|
||||||
if let Err(err) = send
|
|
||||||
.send(HostMessage::Lobby(HostLobbyMessage::GetGameSettings))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
on_err.emit(Some(err.into()))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
(state, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
HostState::ScreenOverrides { .. }
|
|
||||||
| HostState::CharacterStates(_)
|
|
||||||
| HostState::Prompt(_, _)
|
|
||||||
| HostState::Result(_, _)
|
|
||||||
| HostState::RoleReveal { .. }
|
|
||||||
| HostState::Day { .. } => (None, false),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
HostEvent::SetErrorCallback(callback) => (None, false),
|
HostEvent::SetErrorCallback(callback) => (None, false),
|
||||||
HostEvent::SetState(state) => (Some(state), true),
|
HostEvent::SetState(state) => (Some(state), true),
|
||||||
HostEvent::Settings(settings) => match &self.state {
|
|
||||||
HostState::Lobby { players, .. } => (
|
|
||||||
Some(HostState::Lobby {
|
|
||||||
settings,
|
|
||||||
players: players.clone(),
|
|
||||||
}),
|
|
||||||
true,
|
|
||||||
),
|
|
||||||
HostState::ScreenOverrides { .. }
|
|
||||||
| HostState::CharacterStates(_)
|
|
||||||
| HostState::Story { .. }
|
|
||||||
| HostState::Prompt(_, _)
|
|
||||||
| HostState::Result(_, _)
|
|
||||||
| HostState::Disconnected
|
|
||||||
| HostState::RoleReveal {
|
|
||||||
ackd: _,
|
|
||||||
waiting: _,
|
|
||||||
}
|
|
||||||
| HostState::GameOver { result: _ }
|
|
||||||
| HostState::Day { .. } => {
|
|
||||||
log::info!("ignoring settings get");
|
|
||||||
(None, false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
HostEvent::Error(_) => (None, false),
|
HostEvent::Error(_) => (None, false),
|
||||||
HostEvent::SetBigScreenState(_) => (None, true),
|
HostEvent::SetBigScreenState(_) => (None, true),
|
||||||
HostEvent::Continue => (None, false),
|
HostEvent::Continue => (None, false),
|
||||||
|
|
@ -825,12 +778,6 @@ impl Host {
|
||||||
{
|
{
|
||||||
log::error!("sending game settings update: {err}");
|
log::error!("sending game settings update: {err}");
|
||||||
}
|
}
|
||||||
if let Err(err) = send
|
|
||||||
.send(HostMessage::Lobby(HostLobbyMessage::GetGameSettings))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
log::error!("sending game settings get: {err}");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let send = self.send.clone();
|
let send = self.send.clone();
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ pub fn Prompt(props: &ActionPromptProps) -> Html {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let (character_id, targets, marked, role_info) = match &props.prompt {
|
let (character_id, targets, marked, role_info) = match &props.prompt {
|
||||||
|
ActionPrompt::BeholderWakes { .. } => return html! {},
|
||||||
ActionPrompt::TraitorIntro { character_id } => {
|
ActionPrompt::TraitorIntro { character_id } => {
|
||||||
return html! {
|
return html! {
|
||||||
<div class="prompt">
|
<div class="prompt">
|
||||||
|
|
@ -221,7 +222,7 @@ pub fn Prompt(props: &ActionPromptProps) -> Html {
|
||||||
marked.iter().cloned().collect::<Box<[CharacterId]>>(),
|
marked.iter().cloned().collect::<Box<[CharacterId]>>(),
|
||||||
html! {{"adjudicator"}},
|
html! {{"adjudicator"}},
|
||||||
),
|
),
|
||||||
ActionPrompt::Beholder {
|
ActionPrompt::BeholderChooses {
|
||||||
character_id,
|
character_id,
|
||||||
living_players,
|
living_players,
|
||||||
marked,
|
marked,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ impl RolePage for ActionPrompt {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
match self {
|
match self {
|
||||||
ActionPrompt::Beholder { character_id, .. } => Rc::new([html! {
|
ActionPrompt::BeholderChooses { character_id, .. } => Rc::new([html! {
|
||||||
<>
|
<>
|
||||||
{ident(character_id)}
|
{ident(character_id)}
|
||||||
<BeholderPage1 />
|
<BeholderPage1 />
|
||||||
|
|
@ -247,6 +247,12 @@ impl RolePage for ActionPrompt {
|
||||||
<BloodletterPage1 />
|
<BloodletterPage1 />
|
||||||
</>
|
</>
|
||||||
}]),
|
}]),
|
||||||
|
ActionPrompt::BeholderWakes { character_id } => Rc::new([html! {
|
||||||
|
<>
|
||||||
|
{ident(character_id)}
|
||||||
|
<BeholderWakePage1 />
|
||||||
|
</>
|
||||||
|
}]),
|
||||||
_ => Rc::new([]),
|
_ => Rc::new([]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Icon, IconSource, IconType};
|
use crate::components::{Icon, IconSource};
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn BeholderPage1() -> Html {
|
pub fn BeholderPage1() -> Html {
|
||||||
|
|
@ -30,6 +30,22 @@ pub fn BeholderPage1() -> Html {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn BeholderWakePage1() -> Html {
|
||||||
|
html! {
|
||||||
|
<div class="role-page">
|
||||||
|
<h1 class="intel">{"BEHOLDER"}</h1>
|
||||||
|
<div class="information intel faint">
|
||||||
|
<h2>{"YOUR TARGET HAS DIED"}</h2>
|
||||||
|
<div class="info-icon-grow">
|
||||||
|
<Icon source={IconSource::Beholder}/>
|
||||||
|
</div>
|
||||||
|
<h2 class="yellow">{"THIS IS THE LAST PIECE OF INFORMATION THEY SAW"}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
pub fn BeholderSawNothing() -> Html {
|
pub fn BeholderSawNothing() -> Html {
|
||||||
html! {
|
html! {
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,9 @@ impl From<ActionResultTitle> for TestScreen {
|
||||||
impl From<ActionPromptTitle> for TestScreen {
|
impl From<ActionPromptTitle> for TestScreen {
|
||||||
fn from(value: ActionPromptTitle) -> Self {
|
fn from(value: ActionPromptTitle) -> Self {
|
||||||
Self::Prompt(match value {
|
Self::Prompt(match value {
|
||||||
|
ActionPromptTitle::BeholderWakes => ActionPrompt::BeholderWakes {
|
||||||
|
character_id: identity(),
|
||||||
|
},
|
||||||
ActionPromptTitle::CoverOfDarkness => ActionPrompt::CoverOfDarkness,
|
ActionPromptTitle::CoverOfDarkness => ActionPrompt::CoverOfDarkness,
|
||||||
ActionPromptTitle::WolvesIntro => ActionPrompt::WolvesIntro {
|
ActionPromptTitle::WolvesIntro => ActionPrompt::WolvesIntro {
|
||||||
wolves: identities(5)
|
wolves: identities(5)
|
||||||
|
|
@ -327,7 +330,7 @@ impl From<ActionPromptTitle> for TestScreen {
|
||||||
dead_players: identities(20),
|
dead_players: identities(20),
|
||||||
marked: None,
|
marked: None,
|
||||||
},
|
},
|
||||||
ActionPromptTitle::Beholder => ActionPrompt::Beholder {
|
ActionPromptTitle::BeholderChooses => ActionPrompt::BeholderChooses {
|
||||||
character_id: identities(1).into_iter().next().unwrap(),
|
character_id: identities(1).into_iter().next().unwrap(),
|
||||||
living_players: identities(20),
|
living_players: identities(20),
|
||||||
marked: None,
|
marked: None,
|
||||||
|
|
@ -417,13 +420,14 @@ fn prompt_class(prompt: &ActionPrompt) -> Option<&'static str> {
|
||||||
ActionPrompt::ElderReveal { .. }
|
ActionPrompt::ElderReveal { .. }
|
||||||
| ActionPrompt::RoleChange { .. }
|
| ActionPrompt::RoleChange { .. }
|
||||||
| ActionPrompt::CoverOfDarkness => None,
|
| ActionPrompt::CoverOfDarkness => None,
|
||||||
ActionPrompt::Seer { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::Seer { .. }
|
||||||
| ActionPrompt::Arcanist { .. }
|
| ActionPrompt::Arcanist { .. }
|
||||||
| ActionPrompt::Gravedigger { .. }
|
| ActionPrompt::Gravedigger { .. }
|
||||||
| ActionPrompt::Adjudicator { .. }
|
| ActionPrompt::Adjudicator { .. }
|
||||||
| ActionPrompt::PowerSeer { .. }
|
| ActionPrompt::PowerSeer { .. }
|
||||||
| ActionPrompt::Mortician { .. }
|
| ActionPrompt::Mortician { .. }
|
||||||
| ActionPrompt::Beholder { .. }
|
| ActionPrompt::BeholderChooses { .. }
|
||||||
| ActionPrompt::MasonsWake { .. }
|
| ActionPrompt::MasonsWake { .. }
|
||||||
| ActionPrompt::MasonLeaderRecruit { .. }
|
| ActionPrompt::MasonLeaderRecruit { .. }
|
||||||
| ActionPrompt::Empath { .. } => Some("intel"),
|
| ActionPrompt::Empath { .. } => Some("intel"),
|
||||||
|
|
|
||||||
|
|
@ -304,14 +304,15 @@ pub fn PromptScreenTest(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionPrompt::Protector { .. }
|
ActionPrompt::BeholderWakes { .. }
|
||||||
|
| ActionPrompt::Protector { .. }
|
||||||
| ActionPrompt::Arcanist { .. }
|
| ActionPrompt::Arcanist { .. }
|
||||||
| ActionPrompt::Gravedigger { .. }
|
| ActionPrompt::Gravedigger { .. }
|
||||||
| ActionPrompt::Militia { .. }
|
| ActionPrompt::Militia { .. }
|
||||||
| ActionPrompt::Adjudicator { .. }
|
| ActionPrompt::Adjudicator { .. }
|
||||||
| ActionPrompt::PowerSeer { .. }
|
| ActionPrompt::PowerSeer { .. }
|
||||||
| ActionPrompt::Mortician { .. }
|
| ActionPrompt::Mortician { .. }
|
||||||
| ActionPrompt::Beholder { .. }
|
| ActionPrompt::BeholderChooses { .. }
|
||||||
| ActionPrompt::Empath { .. }
|
| ActionPrompt::Empath { .. }
|
||||||
| ActionPrompt::Vindicator { .. }
|
| ActionPrompt::Vindicator { .. }
|
||||||
| ActionPrompt::PyreMaster { .. }
|
| ActionPrompt::PyreMaster { .. }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue