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

View File

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

View File

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

View File

@ -45,7 +45,13 @@ fn nothing_on_wolf() {
game.next().title().empath();
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.next_expect_day();
@ -105,7 +111,13 @@ fn takes_on_scapegoats_curse() {
game.character_by_player_id(scapegoat_player_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.next().title().seer();
@ -181,7 +193,13 @@ fn takes_on_scapegoat_aura_curse() {
game.next().title().empath();
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.next_expect_day();
@ -222,7 +240,13 @@ fn takes_on_scapegoat_aura_curse_no_role_change() {
game.next().title().empath();
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.next_expect_day();

View File

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

View File

@ -156,7 +156,7 @@ nav.host-nav {
&:hover {
background-color: white;
color: invert(#cccccc);
color: color.invert(#cccccc);
}
}
@ -952,7 +952,7 @@ error {
flex-direction: row;
flex-wrap: wrap;
gap: 2px;
font-size: 1rem;
font-size: 1em;
margin: 0px;
padding: 0px;
align-items: center;
@ -2159,6 +2159,18 @@ li.choice {
justify-content: center;
align-items: center;
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 {
@ -2248,6 +2260,7 @@ li.choice {
flex-direction: column;
flex-wrap: nowrap;
height: 100%;
align-items: center;
justify-content: space-around;
}
@ -2287,6 +2300,7 @@ li.choice {
flex-direction: column;
flex-wrap: nowrap;
font-weight: bold;
font-size: 0.5em;
gap: 10px;
}
@ -2368,9 +2382,13 @@ li.choice {
}
.information {
font-size: 1.0rem;
font-size: 2.0rem;
font-stretch: condensed;
padding-left: 5%;
padding-right: 5%;
text-align: center;
display: flex;
flex-direction: column;
@ -2397,6 +2415,30 @@ li.choice {
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 {
@ -2594,7 +2636,7 @@ dialog::backdrop {
font-size: 2em;
}
button {
button:not(:hover) {
color: white;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,15 +20,24 @@ pub fn ElderPage1() -> Html {
<div class="role-page">
<h1 class="starts-as-villager">{"ELDER"}</h1>
<div class="information starts-as-villager faint">
<h2>{"YOU ARE THE ELDER"}</h2>
<h4 class="yellow">
{"YOU ARE THE ELDER"}
<span class="yellow">
{"IF YOU ARE EXECUTED BY THE VILLAGE FROM NOW ON "}
{"ALL POWER ROLES WILL BE LOST"}
</h4>
<h4>
</span>
</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 "}
{"DEATH — THIS MAY OR MAY NOT STILL BE INTACT"}
</h4>
</div>
</div>
}

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn HunterPage1() -> Html {
@ -22,14 +22,12 @@ pub fn HunterPage1() -> Html {
<div class="role-page">
<h1 class="offensive">{"HUNTER"}</h1>
<div class="information offensive faint">
<h2>{"SET A HUNTER'S TRAP ON A PLAYER"}</h2>
<div class="info-icon-grow">
<Icon source={IconSource::Hunter}/>
</div>
<h3 class="yellow">
{"SET A HUNTER'S TRAP ON A PLAYER"}
<Icon source={IconSource::Hunter} icon_type={IconType::Informational}/>
<span class="yellow">
{"IF YOU DIE TONIGHT, OR ARE EXECUTED TOMORROW "}
{"THIS PLAYER WILL DIE AT NIGHT"}
</h3>
</span>
</div>
</div>
}

View File

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

View File

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

View File

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

View File

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

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn MilitiaPage1() -> Html {
@ -22,18 +22,16 @@ pub fn MilitiaPage1() -> Html {
<div class="role-page">
<h1 class="offensive">{"MILITIA"}</h1>
<div class="information offensive faint">
<h2>
<span>
{"IF YOU WISH TO USE YOUR "}
<span class="yellow">{"ONCE PER GAME"}</span>
{" KILL ABILITY"}
</h2>
<div class="info-icon-grow">
<Icon source={IconSource::Sword}/>
</div>
<h3 class="yellow">
{"POINT AT YOUR TARGET "}
</span>
<Icon source={IconSource::Sword} icon_type={IconType::Informational}/>
<span class="yellow">
{"PICK A PLAYER "}
{"OR GO BACK TO SLEEP"}
</h3>
</span>
</div>
</div>
}

View File

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

View File

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

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn ProtectorPage1() -> Html {
@ -22,11 +22,9 @@ pub fn ProtectorPage1() -> Html {
<div class="role-page">
<h1 class="defensive">{"PROTECTOR"}</h1>
<div class="information defensive faint">
<h2>{"PICK A PLAYER"}</h2>
<div class="info-icon-grow">
<Icon source={IconSource::Shield} />
</div>
<h3 class="yellow">{"YOU WILL PROTECT THEM FROM A DEATH TONIGHT"}</h3>
{"PICK A PLAYER"}
<Icon source={IconSource::Shield} icon_type={IconType::Informational}/>
<span class="yellow">{"YOU WILL PROTECT THEM FROM A DEATH TONIGHT"}</span>
</div>
</div>
}

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn PyremasterPage1() -> Html {
@ -22,16 +22,14 @@ pub fn PyremasterPage1() -> Html {
<div class="role-page">
<h1 class="offensive">{"PYREMASTER"}</h1>
<div class="information offensive faint">
<h3>{"YOU CAN CHOOSE TO THROW A PLAYER ON THE PYRE"}</h3>
<div class="info-icon-grow">
<Icon source={IconSource::Pyremaster}/>
</div>
<h3>
{"YOU CAN CHOOSE TO THROW A PLAYER ON THE PYRE"}
<Icon source={IconSource::Pyremaster} icon_type={IconType::Informational}/>
<span>
{"IF YOU KILL "}
<span class="yellow">{"TWO"}</span>
{" GOOD VILLAGERS LIKE THIS "}
{"YOU WILL DIE AS WELL"}
</h3>
</span>
</div>
</div>
}

View File

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

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn VindicatorPage1() -> Html {
@ -22,12 +22,9 @@ pub fn VindicatorPage1() -> Html {
<div class="role-page">
<h1 class="defensive">{"VINDICATOR"}</h1>
<div class="information defensive faint">
<h3>{"A VILLAGER WAS EXECUTED"}</h3>
<h2>{"PICK A PLAYER"}</h2>
<div class="info-icon-grow">
<Icon source={IconSource::Vindicator} />
</div>
<h3 class="yellow">{"YOU WILL PROTECT THEM FROM A DEATH TONIGHT"}</h3>
<span>{"A VILLAGER WAS EXECUTED"}</span>
<Icon source={IconSource::Vindicator} icon_type={IconType::Fit}/>
<span class="yellow">{"PICK A PLAYER TO PROTECT FROM A DEATH TONIGHT"}</span>
</div>
</div>
}

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn RoleblockPage() -> Html {
@ -22,11 +22,9 @@ pub fn RoleblockPage() -> Html {
<div class="role-page">
<h1 class="wolves">{"ROLE BLOCKED"}</h1>
<div class="information wolves faint">
<h2>{"YOU WERE ROLE BLOCKED"}</h2>
<div class="info-icon-grow">
<Icon source={IconSource::Roleblock} />
</div>
<h3 class="yellow">{"YOUR NIGHT ACTION DID NOT TAKE PLACE"}</h3>
{"YOU WERE ROLE BLOCKED"}
<Icon source={IconSource::Roleblock} icon_type={IconType::Informational}/>
<span class="yellow">{"YOUR NIGHT ACTION DID NOT TAKE PLACE"}</span>
</div>
</div>
}

View File

@ -14,7 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource};
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn ShiftFailed() -> Html {
@ -22,13 +22,9 @@ pub fn ShiftFailed() -> Html {
<div class="role-page">
<h1 class="wolves">{"SHIFT FAILED"}</h1>
<div class={classes!("information", "wolves", "faint")}>
<h2>{"YOUR SHIFT HAS FAILED"}</h2>
<div class="info-icon-grow">
<Icon source={IconSource::RedX}/>
</div>
<h3>
{"YOUR SHIFT HAS FAILED"}
<Icon source={IconSource::RedX} icon_type={IconType::Informational}/>
{"YOU RETAIN YOUR SHAPESHIFT ABILITY"}
</h3>
</div>
</div>
}

View File

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

View File

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

View File

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