werewolves/werewolves/src/components/character.rs

145 lines
4.5 KiB
Rust
Raw Normal View History

2026-01-09 02:03:48 +00:00
use core::ops::Not;
// Copyright (C) 2025-2026 Emilis Bliūdžius
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// 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};
2026-01-09 02:03:48 +00:00
use werewolves_proto::{
character::Character,
game::{GameTime, SetupRole},
message::CharacterIdentity,
};
use yew::prelude::*;
2026-01-09 02:03:48 +00:00
use crate::{
class::Class,
components::{Icon, IconSource, IconType, PartialAssociatedIcon},
};
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct CharacterCardProps {
pub char: Character,
#[prop_or_default]
pub dead: bool,
#[prop_or_default]
pub faint: bool,
}
#[function_component]
pub fn CharacterCard(CharacterCardProps { faint, char, dead }: &CharacterCardProps) -> Html {
let class = Into::<SetupRole>::into(char.role_title())
.category()
.class();
let role = char.role_title().to_string().to_case(Case::Title);
let ident = char.identity();
let pronouns = ident.pronouns.as_ref().map(|p| {
html! {
<span class="pronouns">{"("}{p}{")"}</span>
}
});
let name = ident.name.clone();
let source = char.role_title().icon().unwrap_or(if char.is_village() {
IconSource::Village
} else {
IconSource::Wolves
});
let dead = dead.then(|| {
html! {
<Icon source={IconSource::Skull} icon_type={IconType::Small}/>
}
});
let faint = faint.then_some("faint");
html! {
<span class={classes!("character-span", class, faint)}>
<span class={classes!("role", class, faint)}>
<div>
{dead}
<Icon source={source} icon_type={IconType::Small}/>
</div>
{role}
</span>
<span class={classes!("number")}><b>{ident.number.get()}</b></span>
<span class="name">{name}</span>
{pronouns}
</span>
}
}
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct CharacterTargetCardProps {
pub ident: CharacterIdentity,
2025-11-17 20:03:08 +00:00
#[prop_or_default]
pub classes: Classes,
}
#[function_component]
2025-11-17 20:03:08 +00:00
pub fn CharacterTargetCard(
CharacterTargetCardProps { ident, classes }: &CharacterTargetCardProps,
) -> Html {
let name = ident.name.clone();
html! {
2025-11-17 20:03:08 +00:00
<span class={classes!("character-span", classes.clone())}>
<span class={classes!("number")}><b>{ident.number.get()}</b></span>
<span class="name">{name}</span>
</span>
}
}
2026-01-09 02:03:48 +00:00
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct CharacterHighlightProps {
pub char: Character,
pub time: GameTime,
}
#[function_component]
pub fn CharacterHighlight(
CharacterHighlightProps { char, time }: &CharacterHighlightProps,
) -> Html {
let class = char.class().map(|c| format!("{c}-highlight"));
let icon = char
.role_title()
.icon()
.map(|source| {
html! {
<Icon source={source} icon_type={IconType::Fit}/>
}
})
.unwrap_or(html! {
<div class="icon-spacer"/>
});
let dead = char.died_to().and_then(|died_to| {
let night = died_to.night();
let day = died_to.day();
match (time, day, night) {
(GameTime::Day { number }, Some(day), None) => number.get() > day.get(),
(GameTime::Night { number }, None, Some(night)) => *number > night,
(GameTime::Day { number }, None, Some(night)) => number.get() > night,
(GameTime::Night { number }, Some(day), None) => *number >= day.get(),
(_, None, None) | (_, Some(_), Some(_)) => true,
}
.then_some("dead")
});
let role_text = char.role_title().to_string().to_case(Case::Title);
html! {
<span class={classes!("highlight-span", class)} role={role_text}>
{icon}
<span class={classes!("number")}><b>{char.number().get()}</b></span>
<span class={classes!("name", dead)}>{char.name()}</span>
</span>
}
}