seer false positives, player list styling

This commit is contained in:
emilis 2025-10-14 17:51:25 +01:00
parent cb69649fe4
commit eaaf0ab2b7
No known key found for this signature in database
8 changed files with 208 additions and 55 deletions

View File

@ -404,12 +404,40 @@ impl Role {
}
}
impl RoleTitle {
pub fn falsely_appear_village() -> Box<[RoleTitle]> {
Self::ALL
.iter()
.copied()
.filter(|r| r.wolf() && r.alignment().village())
.collect()
}
pub fn falsely_appear_wolf() -> Box<[RoleTitle]> {
Self::ALL
.iter()
.copied()
.filter(|r| !r.wolf() && r.alignment().wolves())
.collect()
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum Alignment {
Village,
Wolves,
}
impl Alignment {
pub const fn village(&self) -> bool {
matches!(self, Alignment::Village)
}
pub const fn wolves(&self) -> bool {
matches!(self, Alignment::Wolves)
}
}
impl Display for Alignment {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {

View File

@ -143,6 +143,32 @@ nav.debug-nav {
}
.player-list {
padding-bottom: 20px;
display: flex;
gap: 10px;
justify-items: center;
justify-content: space-evenly;
}
.targets {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
align-content: center;
gap: 10px;
justify-content: space-evenly;
&>* {
// min-width: 15vw;
// min-height: 12vh;
// font-size: 3em;
flex-grow: 1;
}
}
.lobby-player-list {
padding-bottom: 80px;
display: flex;
flex-direction: row;
@ -341,22 +367,28 @@ button {
}
.wolves-intro {
@extend .column-list;
align-content: center;
width: 100%;
.wolves-list {
flex-wrap: wrap;
flex-direction: row;
justify-content: space-evenly;
flex: 1 1 0;
}
& button {
align-self: center;
}
}
.wolves-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
min-height: 70vh;
align-items: center;
&>* {
height: max-content;
}
.identity {
font-size: 1.5em;
}
}
.character {
text-align: center;
border: 3px solid rgba(0, 0, 0, 0.4);
@ -386,7 +418,8 @@ button {
h1,
h2,
h3,
h4 {
h4,
h5 {
text-align: center;
}
@ -702,19 +735,26 @@ clients {
padding-bottom: 5px;
}
.box {
border: solid 3px;
border-color: #432054;
@media only screen and (max-width : 1899px) {
.content {
margin-left: 5vw;
margin-right: 5vw;
display: flex;
flex-basis: content;
}
}
.content {
margin-left: 5vw;
margin-right: 5vw;
margin-top: 30px;
display: flex;
flex-basis: content;
@media only screen and (min-width : 1900px) {
.content {
margin-left: 5vw;
margin-right: 5vw;
display: flex;
flex-basis: content;
min-height: 100vh;
}
}
.sp-ace {
margin: 10px;
}
@ -948,22 +988,13 @@ input {
width: 100%;
align-items: center;
color: white;
height: 90vh;
height: 100%;
justify-content: center;
$marked_bg: color.change($wolves_color, $alpha: 0.3);
$marked_border: color.change($wolves_color, $alpha: 1.0);
$village_bg: color.change($village_color, $alpha: 0.3);
$village_border: color.change($village_color, $alpha: 1.0);
.targets {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: center;
flex-grow: 1;
align-content: center;
}
.character {
padding: 0.5cm;
@ -1688,3 +1719,43 @@ input {
font-size: 2em;
}
}
.two-column {
display: grid;
grid-template-columns: 3fr 2fr;
}
.role-title-span {
display: grid;
grid-template-columns: 1fr 100fr;
max-height: 2rem;
width: fit-content;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 5px;
padding-right: 10px;
img {
vertical-align: text-bottom;
max-height: 2rem;
padding-left: 10px;
}
text-align: center;
}
.false-positives {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 10px;
}
.bottom-bound {
display: flex;
flex-direction: column;
justify-content: center;
}

View File

@ -472,13 +472,17 @@ impl Component for Host {
<Button on_click={to_big}>{"big screen 📺"}</Button>
}
};
let s = _ctx.link().clone();
let story_on_click = Callback::from(move |_| {
s.send_message(HostEvent::SetState(HostState::Story {
story: crate::clients::host::story_test::test_story(),
page: 0,
}));
});
let story_on_click = if let HostState::Story { .. } = &self.state {
crate::callback::send_message(HostMessage::GetState, self.send.clone())
} else {
let s = _ctx.link().clone();
Callback::from(move |_| {
s.send_message(HostEvent::SetState(HostState::Story {
story: crate::clients::host::story_test::test_story(),
page: 0,
}));
})
};
html! {
<nav class="debug-nav" style="z-index: 3;">
<Button on_click={on_error_click}>{"error"}</Button>

View File

@ -22,9 +22,9 @@ pub fn WolvesIntro(props: &WolvesIntroProps) -> Html {
}
});
html! {
<div class="wolves-intro">
<h2>{"these are the wolves:"}</h2>
<div class="row-list wolves-list">
<div class="role-page wolves-intro">
<h1 class="wolves">{"THESE ARE THE WOLVES"}</h1>
<div class="wolves-list">
{
props.wolves.iter().map(|w| html!{
<div class="character wolves faint">

View File

@ -1,7 +1,11 @@
use werewolves_proto::{game::Category, role};
use convert_case::{Case, Casing};
use werewolves_proto::{
game::{Category, SetupRole},
role::{self, RoleTitle},
};
use yew::prelude::*;
use crate::components::{AssociatedIcon, Icon, IconSource, IconType};
use crate::components::{AssociatedIcon, Icon, IconSource, IconType, PartialAssociatedIcon};
#[derive(Debug, Clone, Copy, PartialEq, Properties)]
pub struct AlignmentSpanProps {
@ -59,3 +63,21 @@ pub fn CategorySpan(
</span>
}
}
#[derive(Debug, Clone, Copy, PartialEq, Properties)]
pub struct RoleTitleSpanProps {
pub role: RoleTitle,
}
#[function_component]
pub fn RoleTitleSpan(RoleTitleSpanProps { role }: &RoleTitleSpanProps) -> Html {
let class = Into::<SetupRole>::into(*role).category().class();
let icon = role.icon().unwrap_or(role.alignment().icon());
let text = role.to_string().to_case(Case::Title);
html! {
<span class={classes!("role-title-span", "faint", class)}>
<Icon source={icon} icon_type={IconType::List}/>
<span>{text}</span>
</span>
}
}

View File

@ -60,6 +60,7 @@ impl IconSource {
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum IconType {
List,
Small,
Informational,
#[default]
@ -69,6 +70,7 @@ pub enum IconType {
impl IconType {
pub const fn class(&self) -> &'static str {
match self {
IconType::List => "icon-in-list",
IconType::Small => "icon",
IconType::Informational => "icon-info",
IconType::RoleCheck => "check-icon",

View File

@ -17,7 +17,7 @@ pub fn Lobby(LobbyProps { players, on_action }: &LobbyProps) -> Html {
html! {
<div class="column-list">
<p style="text-align: center;">{format!("Players in lobby: {}", players.len())}</p>
<div class="player-list">
<div class="lobby-player-list">
{
players
.iter()

View File

@ -1,7 +1,9 @@
use werewolves_proto::role::Alignment;
use werewolves_proto::role::{Alignment, RoleTitle};
use yew::prelude::*;
use crate::components::{AssociatedIcon, Icon, IconSource, IconType};
use crate::components::{
AssociatedIcon, Icon, IconSource, IconType, PartialAssociatedIcon, attributes::RoleTitleSpan,
};
#[function_component]
pub fn SeerPage1() -> Html {
@ -31,18 +33,42 @@ pub fn SeerResult(SeerResultProps { alignment }: &SeerResultProps) -> Html {
Alignment::Village => "VILLAGE",
Alignment::Wolves => "WOLFPACK",
};
let false_positives = match alignment {
Alignment::Village => RoleTitle::falsely_appear_village(),
Alignment::Wolves => RoleTitle::falsely_appear_wolf(),
}
.into_iter()
.map(|role| {
html! {
<RoleTitleSpan role={role}/>
}
})
.collect::<Html>();
html! {
<div class="role-page">
<h1 class="intel">{"SEER"}</h1>
<div class="information intel faint">
<h2>{"YOUR TARGET APPEARS AS"}</h2>
<h4>
<Icon
source={alignment.icon()}
icon_type={IconType::Informational}
/>
</h4>
<h3 class="yellow">{text}</h3>
<div class="two-column">
<div>
<h2>{"YOUR TARGET APPEARS AS"}</h2>
<h4>
<Icon
source={alignment.icon()}
icon_type={IconType::Informational}
/>
</h4>
<h3 class="yellow">{text}</h3>
</div>
<div class="bottom-bound">
<h5>
{"ROLES THAT FALSELY APPEAR AS "}
<span class="yellow">{text}</span>
</h5>
<div class="false-positives yellow">
{false_positives}
</div>
</div>
</div>
</div>
</div>
}