binary choice for shapeshift, added better shapeshift handling
This commit is contained in:
parent
09039c21c1
commit
e704fdca8b
|
|
@ -343,6 +343,57 @@ impl Night {
|
||||||
Ok(new_village)
|
Ok(new_village)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_shapeshift(&mut self, source: &CharacterId) -> Result<ActionResult> {
|
||||||
|
if let Some(kill_target) = self.changes.iter().find_map(|c| match c {
|
||||||
|
NightChange::Kill {
|
||||||
|
target,
|
||||||
|
died_to: DiedTo::Wolfpack { night: _ },
|
||||||
|
} => Some(target.clone()),
|
||||||
|
_ => None,
|
||||||
|
}) {
|
||||||
|
if self.changes.iter().any(|c| match c {
|
||||||
|
NightChange::Protection {
|
||||||
|
target,
|
||||||
|
protection: _,
|
||||||
|
} => target == &kill_target,
|
||||||
|
_ => false,
|
||||||
|
}) {
|
||||||
|
// there is protection, so the kill doesn't happen -> no shapeshift
|
||||||
|
return Ok(ActionResult::GoBackToSleep);
|
||||||
|
}
|
||||||
|
if let Some(kill) = self.changes.iter_mut().find(|c| {
|
||||||
|
matches!(
|
||||||
|
c,
|
||||||
|
NightChange::Kill {
|
||||||
|
target: _,
|
||||||
|
died_to: DiedTo::Wolfpack { night: _ }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}) {
|
||||||
|
*kill = NightChange::Kill {
|
||||||
|
target: source.clone(),
|
||||||
|
died_to: DiedTo::Shapeshift {
|
||||||
|
into: kill_target.clone(),
|
||||||
|
night: NonZeroU8::new(self.night).unwrap(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.changes.push(NightChange::Shapeshift {
|
||||||
|
source: source.clone(),
|
||||||
|
});
|
||||||
|
self.action_queue.push_front((
|
||||||
|
ActionPrompt::RoleChange {
|
||||||
|
new_role: RoleTitle::Werewolf,
|
||||||
|
},
|
||||||
|
self.village
|
||||||
|
.find_by_character_id(&kill_target)
|
||||||
|
.unwrap()
|
||||||
|
.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(ActionResult::GoBackToSleep)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn received_response(&mut self, resp: ActionResponse) -> Result<ActionResult> {
|
pub fn received_response(&mut self, resp: ActionResponse) -> Result<ActionResult> {
|
||||||
if let (
|
if let (
|
||||||
NightState::Active {
|
NightState::Active {
|
||||||
|
|
@ -367,6 +418,10 @@ impl Night {
|
||||||
} => current_result.replace(result.clone()),
|
} => current_result.replace(result.clone()),
|
||||||
NightState::Complete => return Err(GameError::NightOver),
|
NightState::Complete => return Err(GameError::NightOver),
|
||||||
};
|
};
|
||||||
|
if let NightChange::Shapeshift { source } = &change {
|
||||||
|
// needs to be resolved _now_
|
||||||
|
return self.apply_shapeshift(source);
|
||||||
|
}
|
||||||
self.changes.push(change);
|
self.changes.push(change);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,7 @@ pub enum ActionResult {
|
||||||
Seer(Alignment),
|
Seer(Alignment),
|
||||||
Arcanist { same: bool },
|
Arcanist { same: bool },
|
||||||
GraveDigger(Option<RoleTitle>),
|
GraveDigger(Option<RoleTitle>),
|
||||||
WolvesMustBeUnanimous,
|
|
||||||
WaitForOthersToVote,
|
|
||||||
GoBackToSleep,
|
GoBackToSleep,
|
||||||
RoleRevealDone,
|
|
||||||
WolvesIntroDone,
|
WolvesIntroDone,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,9 @@ impl CharacterId {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(uuid::Uuid::new_v4())
|
Self(uuid::Uuid::new_v4())
|
||||||
}
|
}
|
||||||
|
pub const fn from_u128(v: u128) -> Self {
|
||||||
|
Self(uuid::Uuid::from_u128(v))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CharacterId {
|
impl Display for CharacterId {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
use crate::components::Button;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||||
|
pub struct BinaryChoiceProps {
|
||||||
|
pub on_chosen: Callback<bool>,
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Html,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn BinaryChoice(
|
||||||
|
BinaryChoiceProps {
|
||||||
|
on_chosen,
|
||||||
|
children,
|
||||||
|
}: &BinaryChoiceProps,
|
||||||
|
) -> Html {
|
||||||
|
let on_chosen_yes = on_chosen.clone();
|
||||||
|
let yes = move |_| on_chosen_yes.emit(true);
|
||||||
|
let on_chosen = on_chosen.clone();
|
||||||
|
let no = move |_| on_chosen.emit(false);
|
||||||
|
html! {
|
||||||
|
<div class="column-list">
|
||||||
|
{children.clone()}
|
||||||
|
<div class="row-list">
|
||||||
|
<Button on_click={yes}>{"yes"}</Button>
|
||||||
|
<Button on_click={no}>{"no"}</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use core::ops::Not;
|
use core::{num::NonZeroU8, ops::Not};
|
||||||
|
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
message::{
|
message::{
|
||||||
|
|
@ -12,7 +12,7 @@ use yew::prelude::*;
|
||||||
|
|
||||||
use crate::components::{
|
use crate::components::{
|
||||||
Identity,
|
Identity,
|
||||||
action::{SingleTarget, TargetSelection, WolvesIntro},
|
action::{BinaryChoice, SingleTarget, TargetSelection, WolvesIntro},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||||
|
|
@ -162,7 +162,19 @@ pub fn Prompt(props: &ActionPromptProps) -> Html {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionPrompt::Shapeshifter => todo!(),
|
ActionPrompt::Shapeshifter => {
|
||||||
|
let on_complete = props.on_complete.clone();
|
||||||
|
let on_select = move |shift| {
|
||||||
|
on_complete.emit(HostMessage::InGame(HostGameMessage::Night(
|
||||||
|
HostNightMessage::ActionResponse(ActionResponse::Shapeshifter(shift)),
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
html! {
|
||||||
|
<BinaryChoice on_chosen={on_select}>
|
||||||
|
<h2>{"shapeshift?"}</h2>
|
||||||
|
</BinaryChoice>
|
||||||
|
}
|
||||||
|
}
|
||||||
ActionPrompt::AlphaWolf { living_villagers } => {
|
ActionPrompt::AlphaWolf { living_villagers } => {
|
||||||
let on_complete = props.on_complete.clone();
|
let on_complete = props.on_complete.clone();
|
||||||
let on_select = props.big_screen.not().then(|| {
|
let on_select = props.big_screen.not().then(|| {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use core::ops::Not;
|
use core::ops::Not;
|
||||||
|
|
||||||
|
use convert_case::{Case, Casing};
|
||||||
use werewolves_proto::{
|
use werewolves_proto::{
|
||||||
message::{
|
message::{
|
||||||
PublicIdentity,
|
PublicIdentity,
|
||||||
host::{HostGameMessage, HostMessage, HostNightMessage},
|
host::{HostGameMessage, HostMessage, HostNightMessage},
|
||||||
night::{ActionPrompt, ActionResponse, ActionResult},
|
night::ActionResult,
|
||||||
},
|
},
|
||||||
role::Alignment,
|
role::Alignment,
|
||||||
};
|
};
|
||||||
|
|
@ -58,10 +59,30 @@ pub fn ActionResultView(props: &ActionResultProps) -> Html {
|
||||||
{cont}
|
{cont}
|
||||||
</div>
|
</div>
|
||||||
},
|
},
|
||||||
ActionResult::Arcanist { same } => todo!(),
|
ActionResult::Arcanist { same } => {
|
||||||
ActionResult::GraveDigger(role_title) => todo!(),
|
let outcome = if *same { "same" } else { "different" };
|
||||||
ActionResult::WolvesMustBeUnanimous => todo!(),
|
html! {
|
||||||
ActionResult::WaitForOthersToVote => todo!(),
|
<div class="result">
|
||||||
|
{ident}
|
||||||
|
<h2>{"the alignments are:"}</h2>
|
||||||
|
<p>{outcome}</p>
|
||||||
|
{cont}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionResult::GraveDigger(role_title) => {
|
||||||
|
let dig = role_title
|
||||||
|
.map(|r| r.to_string().to_case(Case::Title))
|
||||||
|
.unwrap_or_else(|| String::from("an empty grave"));
|
||||||
|
html! {
|
||||||
|
<div class="result">
|
||||||
|
{ident}
|
||||||
|
<h2>{"you see:"}</h2>
|
||||||
|
<p>{dig}</p>
|
||||||
|
{cont}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
ActionResult::GoBackToSleep => {
|
ActionResult::GoBackToSleep => {
|
||||||
let next = props.big_screen.not().then(|| {
|
let next = props.big_screen.not().then(|| {
|
||||||
let on_complete = props.on_complete.clone();
|
let on_complete = props.on_complete.clone();
|
||||||
|
|
@ -77,7 +98,6 @@ pub fn ActionResultView(props: &ActionResultProps) -> Html {
|
||||||
</CoverOfDarkness>
|
</CoverOfDarkness>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionResult::RoleRevealDone => todo!(),
|
|
||||||
ActionResult::WolvesIntroDone => {
|
ActionResult::WolvesIntroDone => {
|
||||||
let on_complete = props.on_complete.clone();
|
let on_complete = props.on_complete.clone();
|
||||||
let next = props.big_screen.not().then(|| {
|
let next = props.big_screen.not().then(|| {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use core::{fmt::Debug, marker::PhantomData, ops::Not};
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use werewolves_proto::{message::Target, player::CharacterId};
|
use werewolves_proto::{message::Target, player::CharacterId};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
@ -93,7 +93,7 @@ impl Component for SingleTarget {
|
||||||
if self.selected.len() > 1 {
|
if self.selected.len() > 1 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let selected = self.selected.iter().next().cloned();
|
let selected = self.selected.first().cloned();
|
||||||
let on_click = on_click.clone();
|
let on_click = on_click.clone();
|
||||||
Some(Callback::from(move |_| on_click.emit(selected.clone())))
|
Some(Callback::from(move |_| on_click.emit(selected.clone())))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue