show targets for intel results

This commit is contained in:
emilis 2026-02-01 21:49:59 +00:00
parent 251d72e2a0
commit e536edfc28
No known key found for this signature in database
40 changed files with 607 additions and 457 deletions

View File

@ -192,9 +192,10 @@ impl Night {
marked: Some(marked), marked: Some(marked),
.. ..
} => { } => {
let alignment = self.character_with_current_auras(*marked)?.alignment(); let target = self.character_with_current_auras(*marked)?;
let alignment = target.alignment();
Ok(ResponseOutcome::ActionComplete(ActionComplete { Ok(ResponseOutcome::ActionComplete(ActionComplete {
result: ActionResult::Seer(alignment), result: ActionResult::Seer(target.identity(), alignment),
change: None, change: None,
secondary_changes: vec![], secondary_changes: vec![],
})) }))
@ -217,11 +218,15 @@ impl Night {
marked: (Some(marked1), Some(marked2)), marked: (Some(marked1), Some(marked2)),
.. ..
} => { } => {
let same = self.character_with_current_auras(*marked1)?.alignment() let target1 = self.character_with_current_auras(*marked1)?;
== self.character_with_current_auras(*marked2)?.alignment(); let target2 = self.character_with_current_auras(*marked2)?;
let same = target1.alignment() == target2.alignment();
Ok(ResponseOutcome::ActionComplete(ActionComplete { Ok(ResponseOutcome::ActionComplete(ActionComplete {
result: ActionResult::Arcanist(AlignmentEq::new(same)), result: ActionResult::Arcanist(
(target1.identity(), target2.identity()),
AlignmentEq::new(same),
),
change: None, change: None,
secondary_changes: vec![], secondary_changes: vec![],
})) }))
@ -234,7 +239,10 @@ impl Night {
.character_with_current_auras(*marked)? .character_with_current_auras(*marked)?
.gravedigger_dig(); .gravedigger_dig();
Ok(ResponseOutcome::ActionComplete(ActionComplete { Ok(ResponseOutcome::ActionComplete(ActionComplete {
result: ActionResult::GraveDigger(dig_role), result: ActionResult::GraveDigger(
self.village.character_by_id(*marked)?.identity(),
dig_role,
),
change: None, change: None,
secondary_changes: vec![], secondary_changes: vec![],
})) }))
@ -423,6 +431,7 @@ impl Night {
.. ..
} => Ok(ActionComplete { } => Ok(ActionComplete {
result: ActionResult::Adjudicator { result: ActionResult::Adjudicator {
target: self.village.character_by_id(*marked)?.identity(),
killer: self.character_with_current_auras(*marked)?.killer(), killer: self.character_with_current_auras(*marked)?.killer(),
}, },
change: None, change: None,
@ -434,6 +443,7 @@ impl Night {
.. ..
} => Ok(ActionComplete { } => Ok(ActionComplete {
result: ActionResult::PowerSeer { result: ActionResult::PowerSeer {
target: self.village.character_by_id(*marked)?.identity(),
powerful: self.character_with_current_auras(*marked)?.powerful(), powerful: self.character_with_current_auras(*marked)?.powerful(),
}, },
change: None, change: None,
@ -445,6 +455,7 @@ impl Night {
.. ..
} => Ok(ActionComplete { } => Ok(ActionComplete {
result: ActionResult::Mortician( result: ActionResult::Mortician(
self.village.character_by_id(*marked)?.identity(),
self.village self.village
.character_by_id(*marked)? .character_by_id(*marked)?
.died_to() .died_to()
@ -507,7 +518,10 @@ impl Night {
let scapegoat = marked.role_title() == RoleTitle::Scapegoat || is_scapegoat_aura; let scapegoat = marked.role_title() == RoleTitle::Scapegoat || is_scapegoat_aura;
Ok(ActionComplete { Ok(ActionComplete {
result: ActionResult::Empath { scapegoat }, result: ActionResult::Empath {
target: marked.identity(),
scapegoat,
},
change: scapegoat.then(|| NightChange::EmpathFoundScapegoat { change: scapegoat.then(|| NightChange::EmpathFoundScapegoat {
empath: character_id.character_id, empath: character_id.character_id,
scapegoat: marked.character_id(), scapegoat: marked.character_id(),
@ -658,36 +672,35 @@ impl Night {
} }
Aura::InevitableScapegoat => { Aura::InevitableScapegoat => {
match &act.result { match &act.result {
ActionResult::Adjudicator { .. } => { ActionResult::Adjudicator { target, .. } => {
act.result = ActionResult::Adjudicator { act.result = ActionResult::Adjudicator {
target: target.clone(),
killer: Killer::Killer, killer: Killer::Killer,
}; };
} }
ActionResult::PowerSeer { .. } => { ActionResult::PowerSeer { target, .. } => {
act.result = ActionResult::PowerSeer { act.result = ActionResult::PowerSeer {
target: target.clone(),
powerful: Powerful::Powerful, powerful: Powerful::Powerful,
} }
} }
ActionResult::Arcanist(_) => { ActionResult::Arcanist((t1, t2), _) => {
let (_, target2) = self let target2 = self.village.character_by_id(t2.character_id)?;
.current_prompt()
.ok_or(GameError::NoCurrentPromptForAura)? let target2_align = target2.alignment();
.0
.marked()
.ok_or(GameError::MustSelectTarget)?;
let target2_align = self
.village
.character_by_id(target2.ok_or(GameError::MustSelectTarget)?)?
.alignment();
let target1_align = Alignment::Wolves; let target1_align = Alignment::Wolves;
act.result = act.result = ActionResult::Arcanist(
ActionResult::Arcanist(if target1_align == target2_align { (t1.clone(), t2.clone()),
if target1_align == target2_align {
AlignmentEq::Same AlignmentEq::Same
} else { } else {
AlignmentEq::Different AlignmentEq::Different
}); },
);
}
ActionResult::Seer(target, _) => {
act.result = ActionResult::Seer(target.clone(), Alignment::Wolves)
} }
ActionResult::Seer(_) => act.result = ActionResult::Seer(Alignment::Wolves),
_ => {} _ => {}
} }
if let Some((marked, _)) = self if let Some((marked, _)) = self

View File

@ -79,14 +79,25 @@ impl NightChoice {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum StoryActionResult { pub enum StoryActionResult {
RoleBlocked, RoleBlocked,
Seer(Alignment), Seer(CharacterId, Alignment),
PowerSeer { powerful: Powerful }, PowerSeer {
Adjudicator { killer: Killer }, target: CharacterId,
Arcanist(AlignmentEq), powerful: Powerful,
GraveDigger(Option<RoleTitle>), },
Mortician(DiedToTitle), Adjudicator {
Insomniac { visits: Box<[CharacterId]> }, target: CharacterId,
Empath { scapegoat: bool }, killer: Killer,
},
Arcanist((CharacterId, CharacterId), AlignmentEq),
GraveDigger(CharacterId, Option<RoleTitle>),
Mortician(CharacterId, DiedToTitle),
Insomniac {
visits: Box<[CharacterId]>,
},
Empath {
target: CharacterId,
scapegoat: bool,
},
BeholderSawNothing, BeholderSawNothing,
BeholderSawEverything, BeholderSawEverything,
Drunk, Drunk,
@ -101,16 +112,31 @@ impl StoryActionResult {
ActionResult::BeholderSawEverything => Self::BeholderSawEverything, ActionResult::BeholderSawEverything => Self::BeholderSawEverything,
ActionResult::Drunk => Self::Drunk, ActionResult::Drunk => Self::Drunk,
ActionResult::RoleBlocked => Self::RoleBlocked, ActionResult::RoleBlocked => Self::RoleBlocked,
ActionResult::Seer(alignment) => Self::Seer(alignment), ActionResult::Seer(target, alignment) => Self::Seer(target.character_id, alignment),
ActionResult::PowerSeer { powerful } => Self::PowerSeer { powerful }, ActionResult::PowerSeer { target, powerful } => Self::PowerSeer {
ActionResult::Adjudicator { killer } => Self::Adjudicator { killer }, powerful,
ActionResult::Arcanist(same) => Self::Arcanist(same), target: target.character_id,
ActionResult::GraveDigger(role_title) => Self::GraveDigger(role_title), },
ActionResult::Mortician(died_to) => Self::Mortician(died_to), ActionResult::Adjudicator { target, killer } => Self::Adjudicator {
killer,
target: target.character_id,
},
ActionResult::Arcanist((target1, target2), same) => {
Self::Arcanist((target1.character_id, target2.character_id), same)
}
ActionResult::GraveDigger(target, role_title) => {
Self::GraveDigger(target.character_id, role_title)
}
ActionResult::Mortician(target, died_to) => {
Self::Mortician(target.character_id, died_to)
}
ActionResult::Insomniac(visits) => Self::Insomniac { ActionResult::Insomniac(visits) => Self::Insomniac {
visits: visits.iter().map(|c| c.character_id).collect(), visits: visits.iter().map(|c| c.character_id).collect(),
}, },
ActionResult::Empath { scapegoat } => Self::Empath { scapegoat }, ActionResult::Empath { target, scapegoat } => Self::Empath {
scapegoat,
target: target.character_id,
},
ActionResult::GoBackToSleep | ActionResult::Continue => return None, ActionResult::GoBackToSleep | ActionResult::Continue => return None,
}) })

View File

@ -206,19 +206,19 @@ impl ActionResultExt for ActionResult {
} }
fn empath(&self) -> bool { fn empath(&self) -> bool {
match self { match self {
Self::Empath { scapegoat } => *scapegoat, Self::Empath { scapegoat, .. } => *scapegoat,
resp => panic!("expected empath, got {resp:?}"), resp => panic!("expected empath, got {resp:?}"),
} }
} }
fn mortician(&self) -> DiedToTitle { fn mortician(&self) -> DiedToTitle {
match self { match self {
Self::Mortician(role) => *role, Self::Mortician(_, role) => *role,
resp => panic!("expected mortician, got {resp:?}"), resp => panic!("expected mortician, got {resp:?}"),
} }
} }
fn gravedigger(&self) -> Option<RoleTitle> { fn gravedigger(&self) -> Option<RoleTitle> {
match self { match self {
Self::GraveDigger(role) => *role, Self::GraveDigger(_, role) => *role,
resp => panic!("expected gravedigger, got {resp:?}"), resp => panic!("expected gravedigger, got {resp:?}"),
} }
} }
@ -227,13 +227,13 @@ impl ActionResultExt for ActionResult {
} }
fn adjudicator(&self) -> Killer { fn adjudicator(&self) -> Killer {
match self { match self {
Self::Adjudicator { killer } => *killer, Self::Adjudicator { killer, .. } => *killer,
resp => panic!("expected adjudicator, got {resp:?}"), resp => panic!("expected adjudicator, got {resp:?}"),
} }
} }
fn power_seer(&self) -> Powerful { fn power_seer(&self) -> Powerful {
match self { match self {
Self::PowerSeer { powerful } => *powerful, Self::PowerSeer { powerful, .. } => *powerful,
resp => panic!("expected power seer, got {resp:?}"), resp => panic!("expected power seer, got {resp:?}"),
} }
} }
@ -247,14 +247,14 @@ impl ActionResultExt for ActionResult {
fn seer(&self) -> Alignment { fn seer(&self) -> Alignment {
match self { match self {
ActionResult::Seer(a) => a.clone(), ActionResult::Seer(_, a) => a.clone(),
_ => panic!("expected a seer result"), _ => panic!("expected a seer result"),
} }
} }
fn arcanist(&self) -> AlignmentEq { fn arcanist(&self) -> AlignmentEq {
match self { match self {
ActionResult::Arcanist(same) => same.clone(), ActionResult::Arcanist(_, same) => same.clone(),
resp => panic!("expected an arcanist result, got {resp:?}"), resp => panic!("expected an arcanist result, got {resp:?}"),
} }
} }

View File

@ -45,7 +45,13 @@ fn nothing_on_wolf() {
game.next().title().empath(); game.next().title().empath();
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(), ActionResult::Empath { scapegoat: false }); assert_eq!(
game.r#continue(),
ActionResult::Empath {
scapegoat: false,
target: game.character_by_player_id(wolf_player_id).identity()
}
);
game.r#continue().sleep(); game.r#continue().sleep();
game.next_expect_day(); game.next_expect_day();
@ -105,7 +111,13 @@ fn takes_on_scapegoats_curse() {
game.character_by_player_id(scapegoat_player_id) game.character_by_player_id(scapegoat_player_id)
.character_id(), .character_id(),
); );
assert_eq!(game.r#continue(), ActionResult::Empath { scapegoat: true }); assert_eq!(
game.r#continue(),
ActionResult::Empath {
scapegoat: true,
target: game.character_by_player_id(scapegoat_player_id).identity()
}
);
game.r#continue().sleep(); game.r#continue().sleep();
game.next().title().seer(); game.next().title().seer();
@ -181,7 +193,13 @@ fn takes_on_scapegoat_aura_curse() {
game.next().title().empath(); game.next().title().empath();
game.mark(game.character_by_player_id(scapegoat).character_id()); game.mark(game.character_by_player_id(scapegoat).character_id());
assert_eq!(game.r#continue(), ActionResult::Empath { scapegoat: true }); assert_eq!(
game.r#continue(),
ActionResult::Empath {
scapegoat: true,
target: game.character_by_player_id(scapegoat).identity()
}
);
game.r#continue().sleep(); game.r#continue().sleep();
game.next_expect_day(); game.next_expect_day();
@ -222,7 +240,13 @@ fn takes_on_scapegoat_aura_curse_no_role_change() {
game.next().title().empath(); game.next().title().empath();
game.mark(game.character_by_player_id(scapegoat).character_id()); game.mark(game.character_by_player_id(scapegoat).character_id());
assert_eq!(game.r#continue(), ActionResult::Empath { scapegoat: true }); assert_eq!(
game.r#continue(),
ActionResult::Empath {
scapegoat: true,
target: game.character_by_player_id(scapegoat).identity()
}
);
game.r#continue().sleep(); game.r#continue().sleep();
game.next_expect_day(); game.next_expect_day();

View File

@ -601,14 +601,23 @@ pub enum ActionResponse {
pub enum ActionResult { pub enum ActionResult {
RoleBlocked, RoleBlocked,
Drunk, Drunk,
Seer(Alignment), Seer(CharacterIdentity, Alignment),
PowerSeer { powerful: Powerful }, PowerSeer {
Adjudicator { killer: Killer }, target: CharacterIdentity,
Arcanist(AlignmentEq), powerful: Powerful,
GraveDigger(Option<RoleTitle>), },
Mortician(DiedToTitle), Adjudicator {
target: CharacterIdentity,
killer: Killer,
},
Arcanist((CharacterIdentity, CharacterIdentity), AlignmentEq),
GraveDigger(CharacterIdentity, Option<RoleTitle>),
Mortician(CharacterIdentity, DiedToTitle),
Insomniac(Visits), Insomniac(Visits),
Empath { scapegoat: bool }, Empath {
target: CharacterIdentity,
scapegoat: bool,
},
BeholderSawNothing, BeholderSawNothing,
BeholderSawEverything, BeholderSawEverything,
GoBackToSleep, GoBackToSleep,
@ -619,16 +628,26 @@ pub enum ActionResult {
impl ActionResult { impl ActionResult {
pub fn insane(&self) -> Option<Self> { pub fn insane(&self) -> Option<Self> {
Some(match self { Some(match self {
ActionResult::Seer(Alignment::Village) => ActionResult::Seer(Alignment::Wolves), ActionResult::Seer(c, Alignment::Village) => {
ActionResult::Seer(Alignment::Traitor) | ActionResult::Seer(Alignment::Wolves) => { ActionResult::Seer(c.clone(), Alignment::Wolves)
ActionResult::Seer(Alignment::Village)
} }
ActionResult::PowerSeer { powerful } => ActionResult::PowerSeer { ActionResult::Seer(c, Alignment::Traitor)
| ActionResult::Seer(c, Alignment::Wolves) => {
ActionResult::Seer(c.clone(), Alignment::Village)
}
ActionResult::PowerSeer { target, powerful } => ActionResult::PowerSeer {
target: target.clone(),
powerful: !*powerful, powerful: !*powerful,
}, },
ActionResult::Adjudicator { killer } => ActionResult::Adjudicator { killer: !*killer }, ActionResult::Adjudicator { target, killer } => ActionResult::Adjudicator {
ActionResult::Arcanist(alignment_eq) => ActionResult::Arcanist(!*alignment_eq), target: target.clone(),
ActionResult::Empath { scapegoat } => ActionResult::Empath { killer: !*killer,
},
ActionResult::Arcanist(targets, alignment_eq) => {
ActionResult::Arcanist(targets.clone(), !*alignment_eq)
}
ActionResult::Empath { target, scapegoat } => ActionResult::Empath {
target: target.clone(),
scapegoat: !*scapegoat, scapegoat: !*scapegoat,
}, },
ActionResult::BeholderSawNothing => ActionResult::BeholderSawEverything, ActionResult::BeholderSawNothing => ActionResult::BeholderSawEverything,
@ -637,8 +656,8 @@ impl ActionResult {
ActionResult::ShiftFailed ActionResult::ShiftFailed
| ActionResult::RoleBlocked | ActionResult::RoleBlocked
| ActionResult::Drunk | ActionResult::Drunk
| ActionResult::GraveDigger(_) | ActionResult::GraveDigger(_, _)
| ActionResult::Mortician(_) | ActionResult::Mortician(_, _)
| ActionResult::Insomniac(_) | ActionResult::Insomniac(_)
| ActionResult::GoBackToSleep | ActionResult::GoBackToSleep
| ActionResult::Continue => return None, | ActionResult::Continue => return None,

View File

@ -156,7 +156,7 @@ nav.host-nav {
&:hover { &:hover {
background-color: white; background-color: white;
color: invert(#cccccc); color: color.invert(#cccccc);
} }
} }
@ -952,7 +952,7 @@ error {
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;
gap: 2px; gap: 2px;
font-size: 1rem; font-size: 1em;
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
align-items: center; align-items: center;
@ -2159,6 +2159,18 @@ li.choice {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
gap: 30px; gap: 30px;
max-height: 40%;
@media only screen and (min-width : 1600px) {
width: 100%;
}
flex-shrink: 1;
img {
max-height: 100%;
flex-shrink: 1;
}
} }
.icon-info { .icon-info {
@ -2248,6 +2260,7 @@ li.choice {
flex-direction: column; flex-direction: column;
flex-wrap: nowrap; flex-wrap: nowrap;
height: 100%; height: 100%;
align-items: center;
justify-content: space-around; justify-content: space-around;
} }
@ -2287,6 +2300,7 @@ li.choice {
flex-direction: column; flex-direction: column;
flex-wrap: nowrap; flex-wrap: nowrap;
font-weight: bold; font-weight: bold;
font-size: 0.5em;
gap: 10px; gap: 10px;
} }
@ -2368,9 +2382,13 @@ li.choice {
} }
.information { .information {
font-size: 1.0rem; font-size: 2.0rem;
font-stretch: condensed;
padding-left: 5%; padding-left: 5%;
padding-right: 5%; padding-right: 5%;
text-align: center;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -2397,6 +2415,30 @@ li.choice {
padding-bottom: 10px; padding-bottom: 10px;
} }
} }
img {
flex-shrink: 1;
flex-grow: 1;
}
.subtext {
font-size: 1.5rem;
}
.arcanist-targets {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 1ch;
font-size: 1.5rem;
align-items: center;
.and {
font-style: italic;
opacity: 50%;
font-size: 0.7em;
}
}
} }
.setup-aura { .setup-aura {
@ -2594,7 +2636,7 @@ dialog::backdrop {
font-size: 2em; font-size: 2em;
} }
button { button:not(:hover) {
color: white; color: white;
} }

View File

@ -25,12 +25,9 @@ use werewolves_proto::{
}; };
use yew::prelude::*; use yew::prelude::*;
use crate::{ use crate::components::{
components::{
Button, CoverOfDarkness, Identity, Button, CoverOfDarkness, Identity,
action::{BinaryChoice, TargetPicker, WolvesIntro}, action::{BinaryChoice, TargetPicker, WolvesIntro},
},
pages::TraitorIntroPage,
}; };
#[derive(Debug, Clone, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
@ -111,24 +108,8 @@ pub fn Prompt(props: &ActionPromptProps) -> Html {
}) })
}); });
let cont = continue_callback.clone().map(|continue_callback| {
html! {
<Button on_click={continue_callback}>
{"continue"}
</Button>
}
});
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::BeholderWakes { .. } | ActionPrompt::TraitorIntro { .. } => return html! {},
ActionPrompt::TraitorIntro { character_id } => {
return html! {
<div class="prompt">
{identity_html(props, Some(character_id))}
<TraitorIntroPage />
{cont}
</div>
};
}
ActionPrompt::CoverOfDarkness => { ActionPrompt::CoverOfDarkness => {
return html! { return html! {
<CoverOfDarkness next={continue_callback}/> <CoverOfDarkness next={continue_callback}/>
@ -415,16 +396,15 @@ pub fn Prompt(props: &ActionPromptProps) -> Html {
{identity_html(props, Some(character_id))} {identity_html(props, Some(character_id))}
<h1 class="wolves">{"SHAPESHIFTER"}</h1> <h1 class="wolves">{"SHAPESHIFTER"}</h1>
<div class="information wolves faint"> <div class="information wolves faint">
<h2> <span>
{"WOULD YOU LIKE TO USE YOUR "} {"WOULD YOU LIKE TO USE YOUR "}
<span class="yellow">{"ONCE PER GAME"}</span> <span class="yellow">{"ONCE PER GAME"}</span>
{" SHAPESHIFT ABILITY?"} {" SHAPESHIFT ABILITY?"}
</h2> </span>
<h2> <span class="subtext">
<span class="yellow">{"YOU WILL DIE"}</span>{", AND THE "} <span class="yellow">{"YOU WILL DIE"}</span>
{"TARGET OF THE WOLFPACK KILL"} {", BUT THE TARGET OF THE WOLFPACK KILL SHALL INSTEAD BECOME A WOLF"}
{" SHALL INSTEAD BECOME A WOLF"} </span>
</h2>
</div> </div>
{choice} {choice}
</div> </div>

View File

@ -70,21 +70,21 @@ pub fn ActionResultView(props: &ActionResultProps) -> Html {
ActionResult::BeholderSawNothing => html! { ActionResult::BeholderSawNothing => html! {
<BeholderSawNothing /> <BeholderSawNothing />
}, },
ActionResult::PowerSeer { powerful } => { ActionResult::PowerSeer { target, powerful } => {
html! { html! {
<PowerSeerResult powerful={*powerful}/> <PowerSeerResult powerful={*powerful} target={target.clone().into_public()}/>
} }
} }
ActionResult::Adjudicator { killer } => { ActionResult::Adjudicator { killer, target } => {
html! { html! {
<AdjudicatorResult killer={*killer}/> <AdjudicatorResult killer={*killer} target={target.clone().into_public()}/>
} }
} }
ActionResult::Mortician(died_to) => html! { ActionResult::Mortician(target, died_to) => html! {
<MorticianResultPage died_to={*died_to}/> <MorticianResultPage died_to={*died_to} target={target.clone().into_public()}/>
}, },
ActionResult::Empath { scapegoat } => html! { ActionResult::Empath { target, scapegoat } => html! {
<EmpathResult scapegoat={*scapegoat}/> <EmpathResult scapegoat={*scapegoat} target={target.clone().into_public()}/>
}, },
ActionResult::Insomniac(visits) => { ActionResult::Insomniac(visits) => {
html! { html! {
@ -96,17 +96,17 @@ pub fn ActionResultView(props: &ActionResultProps) -> Html {
<RoleblockPage /> <RoleblockPage />
} }
} }
ActionResult::Seer(alignment) => html! { ActionResult::Seer(target, alignment) => html! {
<SeerResult alignment={*alignment}/> <SeerResult alignment={*alignment} target={target.clone().into_public()}/>
}, },
ActionResult::Arcanist(same) => { ActionResult::Arcanist((t1, t2), same) => {
html! { html! {
<ArcanistResult alignment_eq={*same}/> <ArcanistResult alignment_eq={*same} targets={(t1.clone().into_public(), t2.clone().into_public())}/>
} }
} }
ActionResult::GraveDigger(role_title) => { ActionResult::GraveDigger(target, role_title) => {
html! { html! {
<GravediggerResultPage role={*role_title}/> <GravediggerResultPage role={*role_title} target={target.clone().into_public()}/>
} }
} }
ActionResult::GoBackToSleep => { ActionResult::GoBackToSleep => {

View File

@ -327,40 +327,40 @@ fn Choice(
<Roleblock>{"Roleblocked"}</Roleblock> <Roleblock>{"Roleblocked"}</Roleblock>
</> </>
}, },
StoryActionResult::Seer(alignment) => html! { StoryActionResult::Seer(_, alignment) => html! {
<> <>
<span>{"and saw"}</span> <span>{"and saw"}</span>
<AlignmentSpan alignment={*alignment}/> <AlignmentSpan alignment={*alignment}/>
</> </>
}, },
StoryActionResult::PowerSeer { powerful } => html! { StoryActionResult::PowerSeer { powerful, .. } => html! {
<> <>
<span>{"and saw"}</span> <span>{"and saw"}</span>
<PowerfulSpan powerful={*powerful}/> <PowerfulSpan powerful={*powerful}/>
</> </>
}, },
StoryActionResult::Adjudicator { killer } => html! { StoryActionResult::Adjudicator { killer, .. } => html! {
<> <>
<span>{"and saw"}</span> <span>{"and saw"}</span>
<KillerSpan killer={*killer}/> <KillerSpan killer={*killer}/>
</> </>
}, },
StoryActionResult::Arcanist(alignment_eq) => html! { StoryActionResult::Arcanist(_, alignment_eq) => html! {
<> <>
<span>{"and saw them as"}</span> <span>{"and saw them as"}</span>
<AlignmentComparisonSpan comparison={*alignment_eq}/> <AlignmentComparisonSpan comparison={*alignment_eq}/>
</> </>
}, },
StoryActionResult::GraveDigger(None) => html! { StoryActionResult::GraveDigger(_, None) => html! {
<span>{"but found an empty grave"}</span> <span>{"but found an empty grave"}</span>
}, },
StoryActionResult::GraveDigger(Some(role_title)) => html! { StoryActionResult::GraveDigger(_, Some(role_title)) => html! {
<> <>
<span>{"as"}</span> <span>{"as"}</span>
<RoleSpan role={*role_title}/> <RoleSpan role={*role_title}/>
</> </>
}, },
StoryActionResult::Mortician(died_to_title) => html! { StoryActionResult::Mortician(_, died_to_title) => html! {
<> <>
<span>{"and found"}</span> <span>{"and found"}</span>
<DiedToSpan died_to={*died_to_title}/> <DiedToSpan died_to={*died_to_title}/>
@ -390,10 +390,14 @@ fn Choice(
}) })
.collect::<Html>() .collect::<Html>()
} }
StoryActionResult::Empath { scapegoat: true } => { StoryActionResult::Empath {
scapegoat: true, ..
} => {
html! {<span>{"and found their scapegoat"}</span>} html! {<span>{"and found their scapegoat"}</span>}
} }
StoryActionResult::Empath { scapegoat: false } => { StoryActionResult::Empath {
scapegoat: false, ..
} => {
html! {<span>{"who was not a scapegoat"}</span>} html! {<span>{"who was not a scapegoat"}</span>}
} }
StoryActionResult::BeholderSawNothing => html! { StoryActionResult::BeholderSawNothing => html! {

View File

@ -652,19 +652,19 @@ impl ActionResultExt for ActionResult {
} }
fn empath(&self) -> bool { fn empath(&self) -> bool {
match self { match self {
Self::Empath { scapegoat } => *scapegoat, Self::Empath { scapegoat, .. } => *scapegoat,
resp => panic!("expected empath, got {resp:?}"), resp => panic!("expected empath, got {resp:?}"),
} }
} }
fn mortician(&self) -> DiedToTitle { fn mortician(&self) -> DiedToTitle {
match self { match self {
Self::Mortician(role) => *role, Self::Mortician(_, role) => *role,
resp => panic!("expected mortician, got {resp:?}"), resp => panic!("expected mortician, got {resp:?}"),
} }
} }
fn gravedigger(&self) -> Option<RoleTitle> { fn gravedigger(&self) -> Option<RoleTitle> {
match self { match self {
Self::GraveDigger(role) => *role, Self::GraveDigger(_, role) => *role,
resp => panic!("expected gravedigger, got {resp:?}"), resp => panic!("expected gravedigger, got {resp:?}"),
} }
} }
@ -673,13 +673,13 @@ impl ActionResultExt for ActionResult {
} }
fn adjudicator(&self) -> Killer { fn adjudicator(&self) -> Killer {
match self { match self {
Self::Adjudicator { killer } => *killer, Self::Adjudicator { killer, .. } => *killer,
resp => panic!("expected adjudicator, got {resp:?}"), resp => panic!("expected adjudicator, got {resp:?}"),
} }
} }
fn power_seer(&self) -> Powerful { fn power_seer(&self) -> Powerful {
match self { match self {
Self::PowerSeer { powerful } => *powerful, Self::PowerSeer { powerful, .. } => *powerful,
resp => panic!("expected power seer, got {resp:?}"), resp => panic!("expected power seer, got {resp:?}"),
} }
} }
@ -693,14 +693,14 @@ impl ActionResultExt for ActionResult {
fn seer(&self) -> Alignment { fn seer(&self) -> Alignment {
match self { match self {
ActionResult::Seer(a) => a.clone(), ActionResult::Seer(_, a) => a.clone(),
_ => panic!("expected a seer result"), _ => panic!("expected a seer result"),
} }
} }
fn arcanist(&self) -> AlignmentEq { fn arcanist(&self) -> AlignmentEq {
match self { match self {
ActionResult::Arcanist(same) => same.clone(), ActionResult::Arcanist(_, same) => same.clone(),
resp => panic!("expected an arcanist result, got {resp:?}"), resp => panic!("expected an arcanist result, got {resp:?}"),
} }
} }

View File

@ -1,7 +1,7 @@
use werewolves_proto::aura::AuraTitle; use werewolves_proto::aura::AuraTitle;
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource, PartialAssociatedIcon}; use crate::components::{Icon, IconSource, IconType, PartialAssociatedIcon};
#[function_component] #[function_component]
pub fn DrunkPage() -> Html { pub fn DrunkPage() -> Html {
@ -10,11 +10,9 @@ pub fn DrunkPage() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="drunk">{"DRUNK"}</h1> <h1 class="drunk">{"DRUNK"}</h1>
<div class="information drunk faint"> <div class="information drunk faint">
<h2>{"YOU GOT DRUNK INSTEAD"}</h2> {"YOU GOT DRUNK INSTEAD"}
<div class="info-icon-grow"> <Icon source={icon} icon_type={IconType::Informational}/>
<Icon source={icon}/> <span class="yellow">{"YOUR NIGHT ACTION DID NOT TAKE PLACE"}</span>
</div>
<h3 class="yellow">{"YOUR NIGHT ACTION DID NOT TAKE PLACE"}</h3>
</div> </div>
</div> </div>
} }

View File

@ -16,7 +16,7 @@ use convert_case::{Case, Casing};
use werewolves_proto::{game::SetupRole, role::RoleTitle}; use werewolves_proto::{game::SetupRole, role::RoleTitle};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, PartialAssociatedIcon}; use crate::components::{Icon, IconType, PartialAssociatedIcon};
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, Copy, PartialEq, Properties)]
pub struct RoleChangePageProps { pub struct RoleChangePageProps {
@ -28,21 +28,19 @@ pub fn RoleChangePage(RoleChangePageProps { role }: &RoleChangePageProps) -> Htm
let class = Into::<SetupRole>::into(*role).category().class(); let class = Into::<SetupRole>::into(*role).category().class();
let icon = role.icon().map(|icon| { let icon = role.icon().map(|icon| {
html! { html! {
<div class="info-icon-grow"> <Icon source={icon} icon_type={IconType::Fit}/>
<Icon source={icon}/>
</div>
} }
}); });
html! { html! {
<div class="role-page"> <div class="role-page">
<h1 class={classes!(class)}>{"ROLE CHANGE"}</h1> <h1 class={classes!(class)}>{"ROLE CHANGE"}</h1>
<div class={classes!("information", class, "faint")}> <div class={classes!("information", class, "faint")}>
<h2>{"YOUR ROLE HAS CHANGED"}</h2> {"YOUR ROLE HAS CHANGED"}
{icon} {icon}
<h3> <span>
<span>{"YOUR NEW ROLE IS "}</span> {"YOUR NEW ROLE IS "}
<span class="yellow">{role.to_string().to_case(Case::Upper)}</span> <span class="yellow">{role.to_string().to_case(Case::Upper)}</span>
</h3> </span>
</div> </div>
</div> </div>
} }

View File

@ -23,7 +23,7 @@ use yew::prelude::*;
use crate::{ use crate::{
components::Identity, components::Identity,
pages::{RoleChangePage, WolfpackKillPage}, pages::{RoleChangePage, TraitorIntroPage1, TraitorIntroPage2, WolfpackKillPage},
}; };
werewolves_macros::include_path!("werewolves/src/pages/role_page"); werewolves_macros::include_path!("werewolves/src/pages/role_page");
@ -123,12 +123,20 @@ impl RolePage for ActionPrompt {
character_id, character_id,
recruits_left, recruits_left,
.. ..
} => Rc::new([html! { } => Rc::new([
html! {
<> <>
{ident(character_id)} {ident(character_id)}
<MasonRecruitPage1 recruits_left={*recruits_left} /> <MasonRecruitPage1 recruits_left={*recruits_left} />
</> </>
}]), },
html! {
<>
{ident(character_id)}
<MasonRecruitPage2 />
</>
},
]),
ActionPrompt::MasonsWake { leader, masons } => Rc::new([html! { ActionPrompt::MasonsWake { leader, masons } => Rc::new([html! {
<> <>
{ident(leader)} {ident(leader)}
@ -147,12 +155,20 @@ impl RolePage for ActionPrompt {
<InsomniacPage1 /> <InsomniacPage1 />
</> </>
}]), }]),
ActionPrompt::ElderReveal { character_id, .. } => Rc::new([html! { ActionPrompt::ElderReveal { character_id, .. } => Rc::new([
html! {
<> <>
{ident(character_id)} {ident(character_id)}
<ElderPage1 /> <ElderPage1 />
</> </>
}]), },
html! {
<>
{ident(character_id)}
<ElderPage2 />
</>
},
]),
ActionPrompt::Guardian { ActionPrompt::Guardian {
character_id, character_id,
previous: None, previous: None,
@ -253,6 +269,20 @@ impl RolePage for ActionPrompt {
<BeholderWakePage1 /> <BeholderWakePage1 />
</> </>
}]), }]),
ActionPrompt::TraitorIntro { character_id } => Rc::new([
html! {
<>
{ident(character_id)}
<TraitorIntroPage1 />
</>
},
html! {
<>
{ident(character_id)}
<TraitorIntroPage2 />
</>
},
]),
_ => Rc::new([]), _ => Rc::new([]),
} }
} }

View File

@ -12,10 +12,10 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 werewolves_proto::role::Killer; use werewolves_proto::{message::PublicIdentity, role::Killer};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource}; use crate::components::{Icon, IconSource, IdentitySpan};
#[function_component] #[function_component]
pub fn AdjudicatorPage1() -> Html { pub fn AdjudicatorPage1() -> Html {
@ -23,23 +23,24 @@ pub fn AdjudicatorPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"ADJUDICATOR"}</h1> <h1 class="defensive">{"ADJUDICATOR"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"PICK A PLAYER"}</h2> <h4>{"PICK A PLAYER"}</h4>
<div class="info-icon-grow">
<Icon source={IconSource::Killer}/> <Icon source={IconSource::Killer}/>
</div> <h4 class="yellow">{"YOU WILL CHECK IF THEY APPEAR AS A KILLER"}</h4>
<h3 class="yellow">{"YOU WILL CHECK IF THEY APPEAR AS A KILLER"}</h3>
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct AdjudicatorResultProps { pub struct AdjudicatorResultProps {
pub killer: Killer, pub killer: Killer,
pub target: PublicIdentity,
} }
#[function_component] #[function_component]
pub fn AdjudicatorResult(AdjudicatorResultProps { killer }: &AdjudicatorResultProps) -> Html { pub fn AdjudicatorResult(
AdjudicatorResultProps { killer, target }: &AdjudicatorResultProps,
) -> Html {
let text = match killer { let text = match killer {
Killer::Killer => "IS A KILLER", Killer::Killer => "IS A KILLER",
Killer::NotKiller => "IS NOT A KILLER", Killer::NotKiller => "IS NOT A KILLER",
@ -60,8 +61,8 @@ pub fn AdjudicatorResult(AdjudicatorResultProps { killer }: &AdjudicatorResultPr
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"ADJUDICATOR"}</h1> <h1 class="defensive">{"ADJUDICATOR"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"YOUR TARGET"}</h2> <IdentitySpan ident={target.clone()}/>
<div class="info-icon-grow">{icon}</div> {icon}
<h3 class="yellow">{text}</h3> <h3 class="yellow">{text}</h3>
</div> </div>
</div> </div>

View File

@ -20,15 +20,12 @@ pub fn AlphaWolfPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="wolves">{"ALPHA WOLF"}</h1> <h1 class="wolves">{"ALPHA WOLF"}</h1>
<div class="information wolves faint"> <div class="information wolves faint">
<h2> <span>
{"IF YOU WISH TO USE YOUR "} {"IF YOU WISH TO USE YOUR "}
<span class="yellow">{"ONCE PER GAME"}</span> <span class="yellow">{"ONCE PER GAME"}</span>
{" KILL ABILITY"} {" KILL ABILITY"}
</h2> </span>
<h2> {"POINT AT YOUR TARGET OR GO BACK TO SLEEP"}
{"POINT AT YOUR TARGET "}
{"OR GO BACK TO SLEEP"}
</h2>
</div> </div>
</div> </div>
} }

View File

@ -12,10 +12,10 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 werewolves_proto::role::AlignmentEq; use werewolves_proto::{message::PublicIdentity, role::AlignmentEq};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource, IconType}; use crate::components::{Icon, IconSource, IconType, IdentitySpan};
#[function_component] #[function_component]
pub fn ArcanistPage1() -> Html { pub fn ArcanistPage1() -> Html {
@ -23,50 +23,59 @@ pub fn ArcanistPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"ARCANIST"}</h1> <h1 class="intel">{"ARCANIST"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"PICK TWO PLAYERS"}</h2> {"PICK TWO PLAYERS"}
<h4 class="icons"> <div class="icons">
<Icon source={IconSource::Village} icon_type={IconType::Informational}/> <Icon source={IconSource::Village}
// <Icon source={IconSource::Village} icon_type={IconType::Informational}/> icon_type={IconType::Informational}
// <Icon source={IconSource::Wolves} icon_type={IconType::Informational}/> />
<Icon source={IconSource::Wolves} icon_type={IconType::Informational}/> <Icon source={IconSource::Wolves}
</h4> icon_type={IconType::Informational}
<h3 class="yellow">{"YOU WILL CHECK IF THEIR ALIGNMENTS ARE THE SAME OR DIFFERENT"}</h3> />
</div>
<span class="yellow">{"YOU WILL COMPARE THEIR ALIGNMENTS"}</span>
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct ArcanistResultProps { pub struct ArcanistResultProps {
pub alignment_eq: AlignmentEq, pub alignment_eq: AlignmentEq,
pub targets: (PublicIdentity, PublicIdentity),
} }
#[function_component] #[function_component]
pub fn ArcanistResult(ArcanistResultProps { alignment_eq }: &ArcanistResultProps) -> Html { pub fn ArcanistResult(
ArcanistResultProps {
alignment_eq,
targets: (t1, t2),
}: &ArcanistResultProps,
) -> Html {
let text = match alignment_eq { let text = match alignment_eq {
AlignmentEq::Same => "THE SAME", AlignmentEq::Same => "ARE THE SAME",
AlignmentEq::Different => "DIFFERENT", AlignmentEq::Different => "ARE DIFFERENT",
}; };
let icons = match alignment_eq { let icons = match alignment_eq {
AlignmentEq::Same => html! { AlignmentEq::Same => html! {
<Icon source={IconSource::Equal} /> <Icon source={IconSource::Equal} icon_type={IconType::Informational} />
}, },
AlignmentEq::Different => html! { AlignmentEq::Different => html! {
<> <Icon source={IconSource::NotEqual} icon_type={IconType::Informational} />
<Icon source={IconSource::Village} />
<Icon source={IconSource::Wolves} />
</>
}, },
}; };
html! { html! {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"ARCANIST"}</h1> <h1 class="intel">{"ARCANIST"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOUR TARGETS APPEAR AS"}</h2> <div class="arcanist-targets">
<div class="info-icon-list-grow"> <IdentitySpan ident={t1.clone()}/>
<span class="and">{"AND"}</span>
<IdentitySpan ident={t2.clone()}/>
</div>
<div class="icons">
{icons} {icons}
</div> </div>
<h1 class="yellow">{text}</h1> <span class="yellow">{text}</span>
</div> </div>
</div> </div>
} }

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn BeholderPage1() -> Html { pub fn BeholderPage1() -> Html {
@ -22,9 +22,10 @@ pub fn BeholderPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"BEHOLDER"}</h1> <h1 class="intel">{"BEHOLDER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"PICK A PLAYER"}</h2> {"PICK A PLAYER"}
<h3>{"YOU WILL SEE WHAT INFORMATION THEY MAY HAVE GATHERED"}</h3> <Icon source={IconSource::Beholder} icon_type={IconType::Informational}/>
<h2 class="yellow">{"SHOULD THEY DIE TONIGHT"}</h2> {"YOU WILL SEE WHAT INFORMATION THEY MAY HAVE GATHERED"}
<span class="yellow">{"SHOULD THEY DIE TONIGHT"}</span>
</div> </div>
</div> </div>
} }
@ -36,11 +37,9 @@ pub fn BeholderWakePage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"BEHOLDER"}</h1> <h1 class="intel">{"BEHOLDER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOUR TARGET HAS DIED"}</h2> {"YOUR TARGET HAS DIED"}
<div class="info-icon-grow"> <Icon source={IconSource::Beholder} icon_type={IconType::Informational}/>
<Icon source={IconSource::Beholder}/> <span class="yellow">{"THIS IS THE LAST PIECE OF INFORMATION THEY SAW"}</span>
</div>
<h2 class="yellow">{"THIS IS THE LAST PIECE OF INFORMATION THEY SAW"}</h2>
</div> </div>
</div> </div>
} }
@ -68,16 +67,14 @@ pub fn BeholderSawEverything() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"BEHOLDER"}</h1> <h1 class="intel">{"BEHOLDER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h1>{"YOUR TARGET HAS DIED"}</h1> {"YOUR TARGET HAS DIED"}
<div class="info-icon-grow"> <Icon source={IconSource::Beholder} icon_type={IconType::Informational}/>
<Icon source={IconSource::Beholder}/> <span>
</div>
<h1>
{"BUT SAW "} {"BUT SAW "}
<em class="red"> <em class="red">
<strong>{"EVERYTHING"}</strong> <strong>{"EVERYTHING"}</strong>
</em> </em>
</h1> </span>
</div> </div>
</div> </div>
} }

View File

@ -22,17 +22,16 @@ pub fn BloodletterPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="wolves">{"BLOODLETTER"}</h1> <h1 class="wolves">{"BLOODLETTER"}</h1>
<div class="information wolves faint"> <div class="information wolves faint">
<h2>{"PICK A PLAYER"}</h2> <span>{"PICK A PLAYER"}</span>
<h3>{"THEY WILL BE COVERED IN WOLF BLOOD"}</h3> <span class="inline-icons">
<h2 class="yellow inline-icons"> {"THEY'LL APPEAR AS A WOLF "}
{"AND APPEAR AS A WOLF "}
<Icon source={IconSource::Wolves} icon_type={IconType::Fit}/> <Icon source={IconSource::Wolves} icon_type={IconType::Fit}/>
{"KILLER"} {"KILLER"}
<Icon source={IconSource::Killer} icon_type={IconType::Fit}/> <Icon source={IconSource::Killer} icon_type={IconType::Fit}/>
{"AND POWERFUL"} {"AND POWERFUL"}
<Icon source={IconSource::Powerful} icon_type={IconType::Fit}/> <Icon source={IconSource::Powerful} icon_type={IconType::Fit}/>
{" IN CHECKS"} {"IN CHECKS FOR 2 NIGHTS"}
</h2> </span>
</div> </div>
</div> </div>
} }

View File

@ -20,9 +20,9 @@ pub fn DirewolfPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="wolves">{"DIREWOLF"}</h1> <h1 class="wolves">{"DIREWOLF"}</h1>
<div class="information wolves faint"> <div class="information wolves faint">
<h2>{"CHOOSE A TARGET"}</h2> {"CHOOSE A TARGET"}
<h3 class="yellow">{"ANY VISITORS TO THIS TARGET WILL BE ROLE BLOCKED"}</h3> <span class="yellow">{"ANY VISITORS TO THIS TARGET WILL BE ROLE BLOCKED"}</span>
<h4>{"YOU CANNOT CHOOSE YOURSELF OR THE SAME TARGET AS LAST NIGHT"}</h4> {"YOU CANNOT CHOOSE YOURSELF OR THE SAME TARGET AS LAST NIGHT"}
</div> </div>
</div> </div>
} }

View File

@ -20,15 +20,24 @@ pub fn ElderPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="starts-as-villager">{"ELDER"}</h1> <h1 class="starts-as-villager">{"ELDER"}</h1>
<div class="information starts-as-villager faint"> <div class="information starts-as-villager faint">
<h2>{"YOU ARE THE ELDER"}</h2> {"YOU ARE THE ELDER"}
<h4 class="yellow"> <span class="yellow">
{"IF YOU ARE EXECUTED BY THE VILLAGE FROM NOW ON "} {"IF YOU ARE EXECUTED BY THE VILLAGE FROM NOW ON "}
{"ALL POWER ROLES WILL BE LOST"} {"ALL POWER ROLES WILL BE LOST"}
</h4> </span>
<h4> </div>
</div>
}
}
#[function_component]
pub fn ElderPage2() -> Html {
html! {
<div class="role-page">
<h1 class="starts-as-villager">{"ELDER"}</h1>
<div class="information starts-as-villager faint">
{"YOU STARTED THE GAME WITH PROTECTION FROM A NIGHT "} {"YOU STARTED THE GAME WITH PROTECTION FROM A NIGHT "}
{"DEATH — THIS MAY OR MAY NOT STILL BE INTACT"} {"DEATH — THIS MAY OR MAY NOT STILL BE INTACT"}
</h4>
</div> </div>
</div> </div>
} }

View File

@ -13,9 +13,10 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 werewolves_proto::message::PublicIdentity;
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource}; use crate::components::{Icon, IconSource, IconType, IdentitySpan};
#[function_component] #[function_component]
pub fn EmpathPage1() -> Html { pub fn EmpathPage1() -> Html {
@ -23,34 +24,35 @@ pub fn EmpathPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"EMPATH"}</h1> <h1 class="intel">{"EMPATH"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"PICK A PLAYER"}</h2> {"PICK A PLAYER"}
<h3 class="yellow">{"YOU WILL CHECK IF THEY ARE THE SCAPEGOAT"}</h3> <span class="yellow">{"YOU WILL CHECK IF THEY ARE THE SCAPEGOAT"}</span>
<h3>{"IF THEY ARE, YOU WILL TAKE ON THEIR CURSE"}</h3> {"IF THEY ARE, YOU WILL TAKE ON THEIR CURSE"}
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct EmpathResultProps { pub struct EmpathResultProps {
pub scapegoat: bool, pub scapegoat: bool,
pub target: PublicIdentity,
} }
#[function_component] #[function_component]
pub fn EmpathResult(EmpathResultProps { scapegoat }: &EmpathResultProps) -> Html { pub fn EmpathResult(EmpathResultProps { scapegoat, target }: &EmpathResultProps) -> Html {
let text = match scapegoat { let text = match scapegoat {
true => "THE SCAPEGOAT", true => "IS THE SCAPEGOAT",
false => "NOT THE SCAPEGOAT", false => "IS NOT THE SCAPEGOAT",
}; };
let icon = match scapegoat { let icon = match scapegoat {
true => html! { true => html! {
<Icon <Icon
source={IconSource::Scapegoat} source={IconSource::Scapegoat} icon_type={IconType::Informational}
/> />
}, },
false => html! { false => html! {
<Icon <Icon
source={IconSource::RedX} source={IconSource::RedX} icon_type={IconType::Informational}
/> />
}, },
}; };
@ -58,11 +60,9 @@ pub fn EmpathResult(EmpathResultProps { scapegoat }: &EmpathResultProps) -> Html
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"EMPATH"}</h1> <h1 class="intel">{"EMPATH"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOUR TARGET IS"}</h2> <IdentitySpan ident={target.clone()}/>
<div class="info-icon-grow">
{icon} {icon}
</div> <span class="yellow">{text}</span>
<h3 class="yellow">{text}</h3>
</div> </div>
</div> </div>
} }

View File

@ -13,10 +13,10 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use werewolves_proto::role::RoleTitle; use werewolves_proto::{message::PublicIdentity, role::RoleTitle};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource, PartialAssociatedIcon}; use crate::components::{Icon, IconSource, IconType, IdentitySpan, PartialAssociatedIcon};
#[function_component] #[function_component]
pub fn GravediggerPage1() -> Html { pub fn GravediggerPage1() -> Html {
@ -24,46 +24,45 @@ pub fn GravediggerPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"GRAVEDIGGER"}</h1> <h1 class="intel">{"GRAVEDIGGER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2> <span>
{"PICK A "} {"PICK A "}
<span class="yellow">{"DEAD"}</span> <span class="yellow">{"DEAD"}</span>
{" PLAYER"} {" PLAYER"}
</h2> </span>
<div class="info-icon-grow"> <Icon source={IconSource::Gravedigger} icon_type={IconType::Informational}/>
<Icon source={IconSource::Gravedigger}/> <span class="yellow">
</div>
<h3 class="yellow">
{"YOU WILL LEARN THEIR ROLE"} {"YOU WILL LEARN THEIR ROLE"}
</h3> </span>
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct GravediggerResultPageProps { pub struct GravediggerResultPageProps {
pub role: Option<RoleTitle>, pub role: Option<RoleTitle>,
pub target: PublicIdentity,
} }
#[function_component] #[function_component]
pub fn GravediggerResultPage( pub fn GravediggerResultPage(
GravediggerResultPageProps { role }: &GravediggerResultPageProps, GravediggerResultPageProps { role, target }: &GravediggerResultPageProps,
) -> Html { ) -> Html {
let text = role let text = role
.as_ref() .as_ref()
.map(|r| { .map(|r| {
html! { html! {
<h4> <>
{"AND FIND A "} {"WAS A "}
<span class="yellow"> <span class="yellow">
{r.to_string().to_case(Case::Upper)} {r.to_string().to_case(Case::Upper)}
</span> </span>
</h4> </>
} }
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {
html! { html! {
<h4>{"BUT INSTEAD YOU FIND AN EMPTY GRAVE"}</h4> {"YOU FIND AN EMPTY GRAVE"}
} }
}); });
let icon = role let icon = role
@ -74,10 +73,8 @@ pub fn GravediggerResultPage(
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"GRAVEDIGGER"}</h1> <h1 class="intel">{"GRAVEDIGGER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOU CHECK THE ROLE OF YOUR TARGET"}</h2> <IdentitySpan ident={target.clone()}/>
<div class="info-icon-grow"> <Icon source={icon} icon_type={IconType::Informational}/>
<Icon source={icon} />
</div>
{text} {text}
</div> </div>
</div> </div>

View File

@ -15,7 +15,7 @@
use werewolves_proto::message::CharacterIdentity; use werewolves_proto::message::CharacterIdentity;
use yew::prelude::*; use yew::prelude::*;
use crate::components::{CharacterTargetCard, Icon, IconSource}; use crate::components::{CharacterTargetCard, Icon, IconSource, IconType};
#[derive(Debug, Clone, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct GuardianPageProps { pub struct GuardianPageProps {
@ -28,11 +28,9 @@ pub fn GuardianPageNoPrevProtect() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"GUARDIAN"}</h1> <h1 class="defensive">{"GUARDIAN"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"PICK A PLAYER"}</h2> {"PICK A PLAYER"}
<div class="info-icon-grow"> <Icon source={IconSource::ShieldAndSword} icon_type={IconType::Informational}/>
<Icon source={IconSource::ShieldAndSword}/> <span class="yellow">{"CHOOSE SOMEONE TO PROTECT FROM DEATH"}</span>
</div>
<h3 class="yellow">{"CHOOSE SOMEONE TO PROTECT FROM DEATH"}</h3>
</div> </div>
</div> </div>
} }
@ -44,8 +42,8 @@ pub fn GuardianPagePreviousProtectSelf() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"GUARDIAN"}</h1> <h1 class="defensive">{"GUARDIAN"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"LAST TIME YOU PROTECTED YOURSELF"}</h2> {"LAST TIME YOU PROTECTED YOURSELF"}
<h3 class="yellow">{"YOU CANNOT PROTECT YOURSELF AGAIN TONIGHT"}</h3> <span class="yellow">{"YOU CANNOT PROTECT YOURSELF AGAIN TONIGHT"}</span>
</div> </div>
</div> </div>
} }
@ -57,14 +55,12 @@ pub fn GuardianPagePreviousProtect1(GuardianPageProps { previous }: &GuardianPag
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"GUARDIAN"}</h1> <h1 class="defensive">{"GUARDIAN"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"LAST TIME YOU PROTECTED"}</h2> {"LAST TIME YOU PROTECTED"}
<div class="info-icon-grow"> <Icon source={IconSource::ShieldAndSword} icon_type={IconType::Informational}/>
<Icon source={IconSource::ShieldAndSword} />
</div>
<div class="info-player-list"> <div class="info-player-list">
<CharacterTargetCard ident={previous.clone()} /> <CharacterTargetCard ident={previous.clone()} />
</div> </div>
<h3 class="yellow">{"IF YOU PROTECT THEM AGAIN, YOU WILL INSTEAD GUARD THEM"}</h3> <span class="yellow">{"IF YOU PROTECT THEM AGAIN, YOU WILL INSTEAD GUARD THEM"}</span>
</div> </div>
</div> </div>
} }
@ -76,14 +72,12 @@ pub fn GuardianPagePreviousProtect2(GuardianPageProps { previous }: &GuardianPag
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"GUARDIAN"}</h1> <h1 class="defensive">{"GUARDIAN"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"LAST TIME YOU PROTECTED"}</h2> {"LAST TIME YOU PROTECTED"}
<div class="info-icon-grow"> <Icon source={IconSource::ShieldAndSword} icon_type={IconType::Informational}/>
<Icon source={IconSource::ShieldAndSword} />
</div>
<div class="info-player-list"> <div class="info-player-list">
<CharacterTargetCard ident={previous.clone()} /> <CharacterTargetCard ident={previous.clone()} />
</div> </div>
<h3 class="yellow">{"IF ATTACKED WHILE GUARDED, YOU AND THEIR ATTACKER WILL INSTEAD DIE"}</h3> <span class="yellow">{"IF ATTACKED WHILE GUARDED, YOU AND THEIR ATTACKER WILL INSTEAD DIE"}</span>
</div> </div>
</div> </div>
} }
@ -95,14 +89,12 @@ pub fn GuardianPagePreviousGuard(GuardianPageProps { previous }: &GuardianPagePr
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"GUARDIAN"}</h1> <h1 class="defensive">{"GUARDIAN"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"LAST TIME YOU GUARDED"}</h2> {"LAST TIME YOU GUARDED"}
<div class="info-icon-grow"> <Icon source={IconSource::ShieldAndSword} icon_type={IconType::Informational}/>
<Icon source={IconSource::ShieldAndSword} />
</div>
<div class="info-player-list"> <div class="info-player-list">
<CharacterTargetCard ident={previous.clone()} /> <CharacterTargetCard ident={previous.clone()} />
</div> </div>
<h3 class="yellow">{"YOU CANNOT PROTECT THEM TONIGHT"}</h3> <span class="yellow">{"YOU CANNOT PROTECT THEM TONIGHT"}</span>
</div> </div>
</div> </div>
} }

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn HunterPage1() -> Html { pub fn HunterPage1() -> Html {
@ -22,14 +22,12 @@ pub fn HunterPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="offensive">{"HUNTER"}</h1> <h1 class="offensive">{"HUNTER"}</h1>
<div class="information offensive faint"> <div class="information offensive faint">
<h2>{"SET A HUNTER'S TRAP ON A PLAYER"}</h2> {"SET A HUNTER'S TRAP ON A PLAYER"}
<div class="info-icon-grow"> <Icon source={IconSource::Hunter} icon_type={IconType::Informational}/>
<Icon source={IconSource::Hunter}/> <span class="yellow">
</div>
<h3 class="yellow">
{"IF YOU DIE TONIGHT, OR ARE EXECUTED TOMORROW "} {"IF YOU DIE TONIGHT, OR ARE EXECUTED TOMORROW "}
{"THIS PLAYER WILL DIE AT NIGHT"} {"THIS PLAYER WILL DIE AT NIGHT"}
</h3> </span>
</div> </div>
</div> </div>
} }

View File

@ -15,7 +15,7 @@
use werewolves_proto::message::night::Visits; use werewolves_proto::message::night::Visits;
use yew::prelude::*; use yew::prelude::*;
use crate::components::{CharacterTargetCard, Icon, IconSource, Identity}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn InsomniacPage1() -> Html { pub fn InsomniacPage1() -> Html {
@ -23,13 +23,9 @@ pub fn InsomniacPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"INSOMNIAC"}</h1> <h1 class="intel">{"INSOMNIAC"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOUR SLEEP IS INTERRUPTED"}</h2> {"YOUR SLEEP IS INTERRUPTED"}
<div class="info-icon-grow"> <Icon source={IconSource::Insomniac} icon_type={IconType::Informational}/>
<Icon source={IconSource::Insomniac}/> {"YOU'VE NOTICED VISITORS IN THE NIGHT"}
</div>
<h3 class="yellow">
{"THE FOLLOWING PEOPLE VISITED YOU TONIGHT"}
</h3>
</div> </div>
</div> </div>
} }
@ -57,7 +53,7 @@ pub fn InsomniacResult(InsomniacResultProps { visits }: &InsomniacResultProps) -
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"INSOMNIAC"}</h1> <h1 class="intel">{"INSOMNIAC"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOU WERE VISITED IN THE NIGHT BY:"}</h2> {"YOU WERE VISITED IN THE NIGHT BY:"}
<div class="info-player-boxes"> <div class="info-player-boxes">
{visitors} {visitors}
</div> </div>

View File

@ -20,13 +20,13 @@ pub fn LoneWolfPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="wolves">{"LONE WOLF"}</h1> <h1 class="wolves">{"LONE WOLF"}</h1>
<div class="information wolves faint"> <div class="information wolves faint">
<h2> <span>
{"YOU MUST KILL TONIGHT IN ANGER OVER A FELLOW "} {"YOU MUST KILL TONIGHT IN ANGER OVER A FELLOW "}
{"WOLF HAVING BEEN SLAIN"} {"WOLF HAVING BEEN SLAIN"}
</h2> </span>
<h3 class="yellow"> <span class="yellow">
{"POINT AT YOUR TARGET "} {"PICK A PLAYER"}
</h3> </span>
</div> </div>
</div> </div>
} }

View File

@ -29,10 +29,10 @@ pub fn MapleWolfPage1(
) -> Html { ) -> Html {
let food_state = if *nights_til_starvation == 0 { let food_state = if *nights_til_starvation == 0 {
html! { html! {
<div> <>
<h3 class="red">{"YOU ARE STARVING"}</h3> <span class="red">{"YOU ARE STARVING"}</span>
<h3>{"IF YOU FAIL TO EAT TONIGHT, YOU WILL DIE"}</h3> {"IF YOU FAIL TO EAT TONIGHT, YOU WILL DIE"}
</div> </>
} }
} else { } else {
let nights = if *nights_til_starvation == 1 { let nights = if *nights_til_starvation == 1 {
@ -49,23 +49,19 @@ pub fn MapleWolfPage1(
} }
}; };
html! { html! {
<h3> <span>
{"IF YOU FAIL TO EAT "} {"IF YOU FAIL TO EAT "}
{nights} {nights}
{"YOU WILL "}<span class="yellow">{"STARVE"}</span> {"YOU WILL "}<span class="yellow">{"STARVE"}</span>
</h3> </span>
} }
}; };
html! { html! {
<div class="role-page"> <div class="role-page">
<h1 class="offensive">{"MAPLE WOLF"}</h1> <h1 class="offensive">{"MAPLE WOLF"}</h1>
<div class="information offensive faint"> <div class="information offensive faint">
<h2>
{"YOU CAN CHOOSE TO EAT A PLAYER TONIGHT"} {"YOU CAN CHOOSE TO EAT A PLAYER TONIGHT"}
</h2>
<div class="info-icon-grow">
<Icon source={IconSource::MapleWolf} icon_type={IconType::Fit}/> <Icon source={IconSource::MapleWolf} icon_type={IconType::Fit}/>
</div>
{food_state} {food_state}
</div> </div>
</div> </div>

View File

@ -47,13 +47,23 @@ pub fn MasonRecruitPage1(
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"MASON LEADER"}</h1> <h1 class="intel">{"MASON LEADER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOU HAVE "}{recruitments}{" LEFT"}</h2> <span>{"YOU HAVE "}{recruitments}{" LEFT"}</span>
<h4> <span>
{"ANYONE YOU RECRUIT INTO THE MASONS WILL WAKE WITH YOU "} {"RECRUITS WILL WAKE WITH YOU EVERY NIGHT"}
<span class="yellow">{"EVERY NIGHT"}</span> {" WHILE THEY ARE ALIVE AND REMAIN VILLAGE ALIGNED"}
{", AS LONG AS THEY ARE ALIVE AND REMAIN VILLAGE ALIGNED"} </span>
</h4> </div>
<h2 class="yellow">{"WOULD YOU LIKE TO RECRUIT TONIGHT?"}</h2> </div>
}
}
#[function_component]
pub fn MasonRecruitPage2() -> Html {
html! {
<div class="role-page">
<h1 class="intel">{"MASON LEADER"}</h1>
<div class="information intel faint">
<span class="yellow">{"WOULD YOU LIKE TO RECRUIT TONIGHT?"}</span>
</div> </div>
</div> </div>
} }
@ -85,7 +95,7 @@ pub fn MasonsWake(MasonsWakeProps { leader, masons }: &MasonsWakeProps) -> Html
<div class="role-page"> <div class="role-page">
<h1 class="intel">{title}</h1> <h1 class="intel">{title}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOU ARE ALL MEMBERS "}</h2> {"THE MASONS CONVENE AT NIGHT"}
<div class="info-player-list masons"> <div class="info-player-list masons">
{masons} {masons}
</div> </div>

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn MilitiaPage1() -> Html { pub fn MilitiaPage1() -> Html {
@ -22,18 +22,16 @@ pub fn MilitiaPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="offensive">{"MILITIA"}</h1> <h1 class="offensive">{"MILITIA"}</h1>
<div class="information offensive faint"> <div class="information offensive faint">
<h2> <span>
{"IF YOU WISH TO USE YOUR "} {"IF YOU WISH TO USE YOUR "}
<span class="yellow">{"ONCE PER GAME"}</span> <span class="yellow">{"ONCE PER GAME"}</span>
{" KILL ABILITY"} {" KILL ABILITY"}
</h2> </span>
<div class="info-icon-grow"> <Icon source={IconSource::Sword} icon_type={IconType::Informational}/>
<Icon source={IconSource::Sword}/> <span class="yellow">
</div> {"PICK A PLAYER "}
<h3 class="yellow">
{"POINT AT YOUR TARGET "}
{"OR GO BACK TO SLEEP"} {"OR GO BACK TO SLEEP"}
</h3> </span>
</div> </div>
</div> </div>
} }

View File

@ -12,10 +12,10 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 werewolves_proto::diedto::DiedToTitle; use werewolves_proto::{diedto::DiedToTitle, message::PublicIdentity};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource, IconType, PartialAssociatedIcon}; use crate::components::{Icon, IconSource, IconType, IdentitySpan, PartialAssociatedIcon};
#[function_component] #[function_component]
pub fn MorticianPage1() -> Html { pub fn MorticianPage1() -> Html {
@ -23,31 +23,26 @@ pub fn MorticianPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"MORTICIAN"}</h1> <h1 class="intel">{"MORTICIAN"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2> <span>{"PICK A "}<span class="yellow">{"DEAD"}</span>{" PLAYER"}</span>
{"PICK A "} <Icon source={IconSource::Mortician} icon_type={IconType::Informational}/>
<span class="yellow">{"DEAD"}</span> <span class="yellow">
{" PLAYER"}
</h2>
<div class="info-icon-grow">
<Icon source={IconSource::Mortician}/>
</div>
<h3 class="yellow">
{"YOU WILL LEARN THE CAUSE "} {"YOU WILL LEARN THE CAUSE "}
{"OF THEIR DEATH"} {"OF THEIR DEATH"}
</h3> </span>
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct MorticianResultPageProps { pub struct MorticianResultPageProps {
pub died_to: DiedToTitle, pub died_to: DiedToTitle,
pub target: PublicIdentity,
} }
#[function_component] #[function_component]
pub fn MorticianResultPage( pub fn MorticianResultPage(
MorticianResultPageProps { died_to }: &MorticianResultPageProps, MorticianResultPageProps { died_to, target }: &MorticianResultPageProps,
) -> Html { ) -> Html {
let text = match died_to { let text = match died_to {
DiedToTitle::Execution => "Execution", DiedToTitle::Execution => "Execution",
@ -69,11 +64,10 @@ pub fn MorticianResultPage(
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"MORTICIAN"}</h1> <h1 class="intel">{"MORTICIAN"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOUR TARGET DIED TO"}</h2> <IdentitySpan ident={target.clone()}/>
<div class="info-icon-grow"> {"DIED TO"}
<Icon source={icon}/> <Icon source={icon} icon_type={IconType::Informational}/>
</div> <span class="yellow">{text}</span>
<h3 class="yellow">{text}</h3>
</div> </div>
</div> </div>
} }

View File

@ -13,10 +13,10 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 werewolves_proto::role::Powerful; use werewolves_proto::{message::PublicIdentity, role::Powerful};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource}; use crate::components::{Icon, IconSource, IdentitySpan};
#[function_component] #[function_component]
pub fn PowerSeerPage1() -> Html { pub fn PowerSeerPage1() -> Html {
@ -24,38 +24,37 @@ pub fn PowerSeerPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"POWER SEER"}</h1> <h1 class="intel">{"POWER SEER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"PICK A PLAYER"}</h2> {"PICK A PLAYER"}
<div class="info-icon-grow"> <div class="info-icon-grow">
<Icon source={IconSource::Powerful} /> <Icon source={IconSource::Powerful} />
</div> </div>
<h3 class="yellow">{"YOU WILL CHECK IF THEY ARE POWERFUL"}</h3> <span class="yellow">{"YOU WILL CHECK IF THEY ARE POWERFUL"}</span>
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct PowerSeerResultProps { pub struct PowerSeerResultProps {
pub powerful: Powerful, pub powerful: Powerful,
pub target: PublicIdentity,
} }
#[function_component] #[function_component]
pub fn PowerSeerResult(PowerSeerResultProps { powerful }: &PowerSeerResultProps) -> Html { pub fn PowerSeerResult(PowerSeerResultProps { powerful, target }: &PowerSeerResultProps) -> Html {
let text = match powerful { let text = match powerful {
Powerful::Powerful => "POWERFUL", Powerful::Powerful => "IS POWERFUL",
Powerful::NotPowerful => "NOT POWERFUL", Powerful::NotPowerful => "IS NOT POWERFUL",
}; };
let icon = match powerful { let icon = match powerful {
Powerful::Powerful => html! { Powerful::Powerful => html! {
<Icon <Icon
source={IconSource::Powerful} source={IconSource::Powerful}
// icon_type={IconType::Informational}
/> />
}, },
Powerful::NotPowerful => html! { Powerful::NotPowerful => html! {
<Icon <Icon
source={IconSource::RedX} source={IconSource::RedX}
// icon_type={IconType::Informational}
/> />
}, },
}; };
@ -63,7 +62,7 @@ pub fn PowerSeerResult(PowerSeerResultProps { powerful }: &PowerSeerResultProps)
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"POWER SEER"}</h1> <h1 class="intel">{"POWER SEER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"YOUR TARGET APPEARS AS"}</h2> <IdentitySpan ident={target.clone()}/>
<div class="info-icon-grow">{icon}</div> <div class="info-icon-grow">{icon}</div>
<h3 class="yellow">{text}</h3> <h3 class="yellow">{text}</h3>
</div> </div>

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn ProtectorPage1() -> Html { pub fn ProtectorPage1() -> Html {
@ -22,11 +22,9 @@ pub fn ProtectorPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"PROTECTOR"}</h1> <h1 class="defensive">{"PROTECTOR"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h2>{"PICK A PLAYER"}</h2> {"PICK A PLAYER"}
<div class="info-icon-grow"> <Icon source={IconSource::Shield} icon_type={IconType::Informational}/>
<Icon source={IconSource::Shield} /> <span class="yellow">{"YOU WILL PROTECT THEM FROM A DEATH TONIGHT"}</span>
</div>
<h3 class="yellow">{"YOU WILL PROTECT THEM FROM A DEATH TONIGHT"}</h3>
</div> </div>
</div> </div>
} }

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn PyremasterPage1() -> Html { pub fn PyremasterPage1() -> Html {
@ -22,16 +22,14 @@ pub fn PyremasterPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="offensive">{"PYREMASTER"}</h1> <h1 class="offensive">{"PYREMASTER"}</h1>
<div class="information offensive faint"> <div class="information offensive faint">
<h3>{"YOU CAN CHOOSE TO THROW A PLAYER ON THE PYRE"}</h3> {"YOU CAN CHOOSE TO THROW A PLAYER ON THE PYRE"}
<div class="info-icon-grow"> <Icon source={IconSource::Pyremaster} icon_type={IconType::Informational}/>
<Icon source={IconSource::Pyremaster}/> <span>
</div>
<h3>
{"IF YOU KILL "} {"IF YOU KILL "}
<span class="yellow">{"TWO"}</span> <span class="yellow">{"TWO"}</span>
{" GOOD VILLAGERS LIKE THIS "} {" GOOD VILLAGERS LIKE THIS "}
{"YOU WILL DIE AS WELL"} {"YOU WILL DIE AS WELL"}
</h3> </span>
</div> </div>
</div> </div>
} }

View File

@ -12,10 +12,15 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// 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 werewolves_proto::role::{Alignment, RoleTitle}; use werewolves_proto::{
message::PublicIdentity,
role::{Alignment, RoleTitle},
};
use yew::prelude::*; use yew::prelude::*;
use crate::components::{AssociatedIcon, Icon, IconSource, IconType, attributes::RoleTitleSpan}; use crate::components::{
AssociatedIcon, Icon, IconSource, IconType, IdentitySpan, attributes::RoleTitleSpan,
};
#[function_component] #[function_component]
pub fn SeerPage1() -> Html { pub fn SeerPage1() -> Html {
@ -23,24 +28,25 @@ pub fn SeerPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="intel">{"SEER"}</h1> <h1 class="intel">{"SEER"}</h1>
<div class="information intel faint"> <div class="information intel faint">
<h2>{"PICK A PLAYER"}</h2> {"PICK A PLAYER"}
<div class="info-icon-list-grow"> <div class="icons">
<Icon source={IconSource::Village} /> <Icon source={IconSource::Village} icon_type={IconType::Informational} />
<Icon source={IconSource::Wolves} /> <Icon source={IconSource::Wolves} icon_type={IconType::Informational} />
</div> </div>
<h3 class="yellow">{"YOU WILL CHECK IF THEY APPEAR AS A VILLAGER OR PART OF THE WOLFPACK"}</h3> <span class="yellow">{"YOU WILL CHECK THEIR ALIGNMENT"}</span>
</div> </div>
</div> </div>
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Properties)] #[derive(Debug, Clone, PartialEq, Properties)]
pub struct SeerResultProps { pub struct SeerResultProps {
pub alignment: Alignment, pub alignment: Alignment,
pub target: PublicIdentity,
} }
#[function_component] #[function_component]
pub fn SeerResult(SeerResultProps { alignment }: &SeerResultProps) -> Html { pub fn SeerResult(SeerResultProps { alignment, target }: &SeerResultProps) -> Html {
let text = match alignment { let text = match alignment {
Alignment::Village => "VILLAGE", Alignment::Village => "VILLAGE",
Alignment::Wolves => "WOLFPACK", Alignment::Wolves => "WOLFPACK",
@ -61,11 +67,9 @@ pub fn SeerResult(SeerResultProps { alignment }: &SeerResultProps) -> Html {
}, },
Alignment::Traitor => html! { Alignment::Traitor => html! {
<div class="bottom-bound"> <div class="bottom-bound">
<h1>
{"THIS PERSON IS A "} {"THIS PERSON IS A "}
<span class="yellow">{"TRAITOR"}</span> <span class="yellow">{"TRAITOR"}</span>
</h1> {"THEY WIN ALONGSIDE EVIL"}
<h3>{"THEY WIN ALONGSIDE EVIL"}</h3>
</div> </div>
}, },
}; };
@ -75,11 +79,9 @@ pub fn SeerResult(SeerResultProps { alignment }: &SeerResultProps) -> Html {
<div class="information intel faint"> <div class="information intel faint">
<div class="two-column"> <div class="two-column">
<div class="seer-check"> <div class="seer-check">
<h2>{"YOUR TARGET APPEARS AS"}</h2> <IdentitySpan ident={target.clone()}/>
<Icon <Icon source={alignment.icon()}/>
source={alignment.icon()} <span class="yellow">{text}</span>
/>
<h3 class="yellow">{text}</h3>
</div> </div>
{additional_info} {additional_info}
</div> </div>
@ -106,16 +108,15 @@ fn FalselyAppearsAs(
.copied() .copied()
.map(|role| { .map(|role| {
html! { html! {
<RoleTitleSpan role={role} icon_type={IconType::Icon15Pct}/> <RoleTitleSpan role={role} //icon_type={IconType::Icon15Pct}
/>
} }
}) })
.collect::<Html>(); .collect::<Html>();
html! { html! {
<div class="bottom-bound"> <div class="bottom-bound">
<h5>
{"ROLES THAT FALSELY APPEAR AS "} {"ROLES THAT FALSELY APPEAR AS "}
<span class="yellow">{*alignment_text}</span> <span class="yellow">{*alignment_text}</span>
</h5>
<div class="false-positives yellow"> <div class="false-positives yellow">
{false_positives} {false_positives}
</div> </div>

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn VindicatorPage1() -> Html { pub fn VindicatorPage1() -> Html {
@ -22,12 +22,9 @@ pub fn VindicatorPage1() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="defensive">{"VINDICATOR"}</h1> <h1 class="defensive">{"VINDICATOR"}</h1>
<div class="information defensive faint"> <div class="information defensive faint">
<h3>{"A VILLAGER WAS EXECUTED"}</h3> <span>{"A VILLAGER WAS EXECUTED"}</span>
<h2>{"PICK A PLAYER"}</h2> <Icon source={IconSource::Vindicator} icon_type={IconType::Fit}/>
<div class="info-icon-grow"> <span class="yellow">{"PICK A PLAYER TO PROTECT FROM A DEATH TONIGHT"}</span>
<Icon source={IconSource::Vindicator} />
</div>
<h3 class="yellow">{"YOU WILL PROTECT THEM FROM A DEATH TONIGHT"}</h3>
</div> </div>
</div> </div>
} }

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn RoleblockPage() -> Html { pub fn RoleblockPage() -> Html {
@ -22,11 +22,9 @@ pub fn RoleblockPage() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="wolves">{"ROLE BLOCKED"}</h1> <h1 class="wolves">{"ROLE BLOCKED"}</h1>
<div class="information wolves faint"> <div class="information wolves faint">
<h2>{"YOU WERE ROLE BLOCKED"}</h2> {"YOU WERE ROLE BLOCKED"}
<div class="info-icon-grow"> <Icon source={IconSource::Roleblock} icon_type={IconType::Informational}/>
<Icon source={IconSource::Roleblock} /> <span class="yellow">{"YOUR NIGHT ACTION DID NOT TAKE PLACE"}</span>
</div>
<h3 class="yellow">{"YOUR NIGHT ACTION DID NOT TAKE PLACE"}</h3>
</div> </div>
</div> </div>
} }

View File

@ -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}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn ShiftFailed() -> Html { pub fn ShiftFailed() -> Html {
@ -22,13 +22,9 @@ pub fn ShiftFailed() -> Html {
<div class="role-page"> <div class="role-page">
<h1 class="wolves">{"SHIFT FAILED"}</h1> <h1 class="wolves">{"SHIFT FAILED"}</h1>
<div class={classes!("information", "wolves", "faint")}> <div class={classes!("information", "wolves", "faint")}>
<h2>{"YOUR SHIFT HAS FAILED"}</h2> {"YOUR SHIFT HAS FAILED"}
<div class="info-icon-grow"> <Icon source={IconSource::RedX} icon_type={IconType::Informational}/>
<Icon source={IconSource::RedX}/>
</div>
<h3>
{"YOU RETAIN YOUR SHAPESHIFT ABILITY"} {"YOU RETAIN YOUR SHAPESHIFT ABILITY"}
</h3>
</div> </div>
</div> </div>
} }

View File

@ -1,22 +1,30 @@
use yew::prelude::*; use yew::prelude::*;
use crate::components::{Icon, IconSource}; use crate::components::{Icon, IconSource, IconType};
#[function_component] #[function_component]
pub fn TraitorIntroPage() -> Html { pub fn TraitorIntroPage1() -> Html {
html! { html! {
<div class="role-page"> <div class="role-page">
<h1 class="traitor">{"TRAITOR"}</h1> <h1 class="traitor">{"DAMNED"}</h1>
<div class="information traitor faint"> <div class="information traitor faint">
<h2>{"YOU ARE A TRAITOR"}</h2> <span class="yellow">{"YOU ARE DAMNED"}</span>
<h3>{"YOU RETAIN YOUR ROLE AND WIN IF EVIL WINS"}</h3> <Icon source={IconSource::Traitor} icon_type={IconType::Informational}/>
<div class="info-icon-grow"> </div>
<Icon source={IconSource::Traitor}/> </div>
</div> }
<h4>{"HOWEVER"}</h4> }
<h2 class="yellow">
{"YOU CONTRIBUTE TO VILLAGE PARITY"} #[function_component]
</h2> pub fn TraitorIntroPage2() -> Html {
html! {
<div class="role-page">
<h1 class="traitor">{"DAMNED"}</h1>
<div class="information traitor faint">
{"YOU RETAIN YOUR ROLE AND WIN IF EVIL WINS"}
<span class="yellow">
{"HOWEVER, YOU CONTRIBUTE TO VILLAGE PARITY"}
</span>
</div> </div>
</div> </div>
} }

View File

@ -136,7 +136,7 @@ pub fn TestScreenSelector(
let class = prompt_class(&prompt); let class = prompt_class(&prompt);
html! { html! {
<li> <li>
<Button on_click={callback} classes={classes!(class, picked_class)}> <Button on_click={callback} classes={classes!(class, picked_class, "hover")}>
{format!("{title:?}")} {format!("{title:?}")}
</Button> </Button>
</li> </li>
@ -172,7 +172,7 @@ pub fn TestScreenSelector(
let class = result_class(&result); let class = result_class(&result);
html! { html! {
<li> <li>
<Button on_click={callback} classes={classes!(picked_class, class)}> <Button on_click={callback} classes={classes!(picked_class, class, "hover")}>
{format!("{title:?}")} {format!("{title:?}")}
</Button> </Button>
</li> </li>
@ -225,18 +225,28 @@ impl From<ActionResultTitle> for TestScreen {
TestScreen::Result(match value { TestScreen::Result(match value {
ActionResultTitle::RoleBlocked => ActionResult::RoleBlocked, ActionResultTitle::RoleBlocked => ActionResult::RoleBlocked,
ActionResultTitle::Drunk => ActionResult::Drunk, ActionResultTitle::Drunk => ActionResult::Drunk,
ActionResultTitle::Seer => ActionResult::Seer(Alignment::Village), ActionResultTitle::Seer => ActionResult::Seer(identity(), Alignment::Village),
ActionResultTitle::PowerSeer => ActionResult::PowerSeer { ActionResultTitle::PowerSeer => ActionResult::PowerSeer {
target: identity(),
powerful: Powerful::Powerful, powerful: Powerful::Powerful,
}, },
ActionResultTitle::Adjudicator => ActionResult::Adjudicator { ActionResultTitle::Adjudicator => ActionResult::Adjudicator {
target: identity(),
killer: Killer::Killer, killer: Killer::Killer,
}, },
ActionResultTitle::Arcanist => ActionResult::Arcanist(AlignmentEq::Same), ActionResultTitle::Arcanist => ActionResult::Arcanist(
ActionResultTitle::GraveDigger => ActionResult::GraveDigger(None), (identity(), identities(2).last().cloned().unwrap()),
ActionResultTitle::Mortician => ActionResult::Mortician(DiedToTitle::Execution), AlignmentEq::Same,
),
ActionResultTitle::GraveDigger => ActionResult::GraveDigger(identity(), None),
ActionResultTitle::Mortician => {
ActionResult::Mortician(identity(), DiedToTitle::Execution)
}
ActionResultTitle::Insomniac => ActionResult::Insomniac(Visits::new(identities(2))), ActionResultTitle::Insomniac => ActionResult::Insomniac(Visits::new(identities(2))),
ActionResultTitle::Empath => ActionResult::Empath { scapegoat: true }, ActionResultTitle::Empath => ActionResult::Empath {
target: identity(),
scapegoat: true,
},
ActionResultTitle::BeholderSawNothing => ActionResult::BeholderSawNothing, ActionResultTitle::BeholderSawNothing => ActionResult::BeholderSawNothing,
ActionResultTitle::BeholderSawEverything => ActionResult::BeholderSawEverything, ActionResultTitle::BeholderSawEverything => ActionResult::BeholderSawEverything,
ActionResultTitle::GoBackToSleep => ActionResult::GoBackToSleep, ActionResultTitle::GoBackToSleep => ActionResult::GoBackToSleep,

View File

@ -49,16 +49,17 @@ pub fn ResultScreenTest(
| ActionResult::Continue | ActionResult::Continue
| ActionResult::Drunk | ActionResult::Drunk
| ActionResult::RoleBlocked => html! {}, | ActionResult::RoleBlocked => html! {},
ActionResult::Seer(alignment) => { ActionResult::Seer(target, alignment) => {
let all = Alignment::ALL let all = Alignment::ALL
.into_iter() .into_iter()
.map(|align| { .map(|align| {
let on_click = { let on_click = {
let target= target.clone();
let send = send.clone(); let send = send.clone();
Callback::from(move |_| { Callback::from(move |_| {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::Seer(align), ActionResult::Seer(target.clone(), align),
)); ));
}) })
}; };
@ -76,14 +77,18 @@ pub fn ResultScreenTest(
</div> </div>
} }
} }
ActionResult::PowerSeer { powerful } => { ActionResult::PowerSeer { target, powerful } => {
let on_toggle = { let on_toggle = {
let set = !*powerful; let set = !*powerful;
let send = send.clone(); let send = send.clone();
let target = target.clone();
Callback::from(move |_| { Callback::from(move |_| {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::PowerSeer { powerful: set }, ActionResult::PowerSeer {
target: target.clone(),
powerful: set,
},
)); ));
}) })
}; };
@ -98,14 +103,18 @@ pub fn ResultScreenTest(
</div> </div>
} }
} }
ActionResult::Adjudicator { killer } => { ActionResult::Adjudicator { target, killer } => {
let on_toggle = { let on_toggle = {
let set = !*killer; let set = !*killer;
let send = send.clone(); let send = send.clone();
let target = target.clone();
Callback::from(move |_| { Callback::from(move |_| {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::Adjudicator { killer: set }, ActionResult::Adjudicator {
target: target.clone(),
killer: set,
},
)); ));
}) })
}; };
@ -120,14 +129,15 @@ pub fn ResultScreenTest(
</div> </div>
} }
} }
ActionResult::Arcanist(alignment_eq) => { ActionResult::Arcanist(targets, alignment_eq) => {
let on_toggle = { let on_toggle = {
let set = !*alignment_eq; let set = !*alignment_eq;
let send = send.clone(); let send = send.clone();
let targets = targets.clone();
Callback::from(move |_| { Callback::from(move |_| {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::Arcanist(set), ActionResult::Arcanist(targets.clone(), set),
)); ));
}) })
}; };
@ -142,7 +152,7 @@ pub fn ResultScreenTest(
</div> </div>
} }
} }
ActionResult::GraveDigger(role_title) => { ActionResult::GraveDigger(target, role_title) => {
let possibilities = [None] let possibilities = [None]
.into_iter() .into_iter()
.chain(RoleTitle::ALL.into_iter().map(Some)) .chain(RoleTitle::ALL.into_iter().map(Some))
@ -162,6 +172,7 @@ pub fn ResultScreenTest(
let on_change_cb = { let on_change_cb = {
let send = send.clone(); let send = send.clone();
let possibilities = possibilities.clone(); let possibilities = possibilities.clone();
let target = target.clone();
Callback::from(move |ev: Event| { Callback::from(move |ev: Event| {
if let Some(select) = ev.target_dyn_into::<HtmlSelectElement>() { if let Some(select) = ev.target_dyn_into::<HtmlSelectElement>() {
let selected = select.selected_index(); let selected = select.selected_index();
@ -171,7 +182,7 @@ pub fn ResultScreenTest(
if let Some(new_role) = possibilities.get(selected as usize) { if let Some(new_role) = possibilities.get(selected as usize) {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::GraveDigger(*new_role), ActionResult::GraveDigger(target.clone(), *new_role),
)); ));
} }
} }
@ -179,6 +190,7 @@ pub fn ResultScreenTest(
}; };
let on_wheel = { let on_wheel = {
let send = send.clone(); let send = send.clone();
let res_target = target.clone();
let possibilities = possibilities.clone(); let possibilities = possibilities.clone();
Callback::from(move |ev: WheelEvent| { Callback::from(move |ev: WheelEvent| {
let Some(target) = ev.target_dyn_into::<HtmlSelectElement>() else { let Some(target) = ev.target_dyn_into::<HtmlSelectElement>() else {
@ -206,7 +218,7 @@ pub fn ResultScreenTest(
if let Some(new_role) = possibilities.get(new_index as usize) { if let Some(new_role) = possibilities.get(new_index as usize) {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::GraveDigger(*new_role), ActionResult::GraveDigger(res_target.clone(), *new_role),
)); ));
} }
}) })
@ -220,7 +232,7 @@ pub fn ResultScreenTest(
</div> </div>
} }
} }
ActionResult::Mortician(died_to_title) => { ActionResult::Mortician(target, died_to_title) => {
let roles = DiedToTitle::ALL let roles = DiedToTitle::ALL
.into_iter() .into_iter()
.map(|died_to| { .map(|died_to| {
@ -231,6 +243,7 @@ pub fn ResultScreenTest(
.collect::<Html>(); .collect::<Html>();
let on_change_cb = { let on_change_cb = {
let send = send.clone(); let send = send.clone();
let target = target.clone();
Callback::from(move |ev: Event| { Callback::from(move |ev: Event| {
if let Some(select) = ev.target_dyn_into::<HtmlSelectElement>() { if let Some(select) = ev.target_dyn_into::<HtmlSelectElement>() {
let selected = select.selected_index(); let selected = select.selected_index();
@ -240,7 +253,7 @@ pub fn ResultScreenTest(
if let Some(died_to) = DiedToTitle::ALL.into_iter().nth(selected as _) { if let Some(died_to) = DiedToTitle::ALL.into_iter().nth(selected as _) {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::Mortician(died_to), ActionResult::Mortician(target.clone(), died_to),
)); ));
} }
} }
@ -248,6 +261,7 @@ pub fn ResultScreenTest(
}; };
let on_wheel = { let on_wheel = {
let send = send.clone(); let send = send.clone();
let res_target = target.clone();
Callback::from(move |ev: WheelEvent| { Callback::from(move |ev: WheelEvent| {
let Some(target) = ev.target_dyn_into::<HtmlSelectElement>() else { let Some(target) = ev.target_dyn_into::<HtmlSelectElement>() else {
return; return;
@ -274,7 +288,7 @@ pub fn ResultScreenTest(
if let Some(died_to) = DiedToTitle::ALL.into_iter().nth(new_index as _) { if let Some(died_to) = DiedToTitle::ALL.into_iter().nth(new_index as _) {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::Mortician(died_to), ActionResult::Mortician(res_target.clone(), died_to),
)); ));
} }
}) })
@ -335,14 +349,18 @@ pub fn ResultScreenTest(
</div> </div>
} }
} }
ActionResult::Empath { scapegoat } => { ActionResult::Empath { target, scapegoat } => {
let on_toggle = { let on_toggle = {
let set = !*scapegoat; let set = !*scapegoat;
let send = send.clone(); let send = send.clone();
let target = target.clone();
Callback::from(move |_| { Callback::from(move |_| {
send.emit(ServerToHostMessage::ActionResult( send.emit(ServerToHostMessage::ActionResult(
None, None,
ActionResult::Empath { scapegoat: set }, ActionResult::Empath {
scapegoat: set,
target: target.clone(),
},
)); ));
}) })
}; };