fix: picking dead players in daytime view

This commit is contained in:
emilis 2026-02-22 23:43:09 +00:00
parent 5a452b220c
commit 567e023a73
No known key found for this signature in database
10 changed files with 54 additions and 12 deletions

View File

@ -1051,6 +1051,11 @@ form {
background-color: color.change($red2, $alpha: 0.3); background-color: color.change($red2, $alpha: 0.3);
border: 1px solid color.change($red2, $alpha: 0.8); border: 1px solid color.change($red2, $alpha: 0.8);
} }
&.dead {
filter: grayscale(100%);
cursor: default;
}
} }
} }

View File

@ -213,9 +213,12 @@ impl Game {
self.process(HostGameMessage::GetState) self.process(HostGameMessage::GetState)
} }
( (
GameState::Day { village: _, marked }, GameState::Day { village, marked },
HostGameMessage::Day(HostDayMessage::MarkForExecution(target)), HostGameMessage::Day(HostDayMessage::MarkForExecution(target)),
) => { ) => {
if village.character_by_id(target)?.died_to().is_some() {
return Err(GameError::CharacterAlreadyDead);
}
match marked match marked
.iter() .iter()
.enumerate() .enumerate()

View File

@ -10,6 +10,8 @@ use leptos_use::{
use_websocket_with_options, use_websocket_with_options,
}; };
use reactive_stores::Store; use reactive_stores::Store;
use sorted_vec::SortedSet;
use werewolves_proto::message::dead::DeadChatMessage;
use werewolves_proto::message::host::ServerToHostMessage; use werewolves_proto::message::host::ServerToHostMessage;
use werewolves_proto::message::{ClientMessage, host::HostMessage}; use werewolves_proto::message::{ClientMessage, host::HostMessage};
use werewolves_proto::message::{IntoClientResponse, WrappedServerMessage}; use werewolves_proto::message::{IntoClientResponse, WrappedServerMessage};
@ -28,6 +30,7 @@ use crate::{
#[component] #[component]
pub fn GamePage(error: WriteSignal<Option<WolfError>>) -> impl IntoView { pub fn GamePage(error: WriteSignal<Option<WolfError>>) -> impl IntoView {
let dead_chat_messages: RwSignal<SortedSet<DeadChatMessage>> = RwSignal::new(SortedSet::new());
move || { move || {
let params = hooks::use_params_map(); let params = hooks::use_params_map();
let auth = expect_context::<Store<AuthContext>>(); let auth = expect_context::<Store<AuthContext>>();
@ -197,12 +200,18 @@ pub fn GamePage(error: WriteSignal<Option<WolfError>>) -> impl IntoView {
view! { view! {
{status} {status}
<HostGamePage error=error message=host_message.into() reply=host_reply.write_only() /> <HostGamePage
error=error
message=host_message.into()
reply=host_reply.write_only()
dead_chat_messages=dead_chat_messages
/>
<PlayerGamePage <PlayerGamePage
error=error error=error
message=player_message.into() message=player_message.into()
reply=player_reply.write_only() reply=player_reply.write_only()
disconnect=disconnect disconnect=disconnect
dead_chat_messages=dead_chat_messages
/> />
} }
} }

View File

@ -1,5 +1,5 @@
werewolves_macros::include_path!("werewolves/src/app/pages/game/big"); werewolves_macros::include_path!("werewolves/src/app/pages/game/big");
use core::str::FromStr; use core::{ops::DerefMut, str::FromStr};
use crate::{ use crate::{
ConsoleLogError, ConsoleLogError,
@ -14,6 +14,7 @@ use crate::{
}, },
}; };
use codee::binary::MsgpackSerdeCodec; use codee::binary::MsgpackSerdeCodec;
use gloo::history::History;
use leptos::prelude::*; use leptos::prelude::*;
use leptos_router::hooks; use leptos_router::hooks;
use leptos_use::{ use leptos_use::{
@ -21,10 +22,12 @@ use leptos_use::{
use_websocket_with_options, use_websocket_with_options,
}; };
use reactive_stores::Store; use reactive_stores::Store;
use sorted_vec::SortedSet;
use werewolves_proto::{ use werewolves_proto::{
game::GameId, game::GameId,
message::{ message::{
CharacterIdentity, IntoClientResponse, WrappedServerMessage, CharacterIdentity, IntoClientResponse, WrappedServerMessage,
dead::DeadChatMessage,
host::ServerToHostMessage, host::ServerToHostMessage,
night::{ActionPrompt, ActionResult}, night::{ActionPrompt, ActionResult},
}, },
@ -51,6 +54,7 @@ enum BigScreenPage {
#[component] #[component]
pub fn BigScreen() -> impl IntoView { pub fn BigScreen() -> impl IntoView {
let dead_chat_messages: RwSignal<SortedSet<DeadChatMessage>> = RwSignal::new(SortedSet::new());
move || { move || {
let params = hooks::use_params_map(); let params = hooks::use_params_map();
let auth = expect_context::<Store<AuthContext>>(); let auth = expect_context::<Store<AuthContext>>();
@ -210,8 +214,15 @@ pub fn BigScreen() -> impl IntoView {
page.set(BigScreenPage::RoleReveal); page.set(BigScreenPage::RoleReveal);
} }
ServerToHostMessage::Story { story, page } => todo!(), ServerToHostMessage::Story { story, page } => todo!(),
ServerToHostMessage::DeadChat(dead_chat_messages) => todo!(), ServerToHostMessage::DeadChat(m) => {
ServerToHostMessage::DeadChatMessage(dead_chat_message) => todo!(), let mut dcm = dead_chat_messages.write();
for msg in m {
dcm.push(msg);
}
}
ServerToHostMessage::DeadChatMessage(msg) => {
dead_chat_messages.write().push(msg);
}
} }
}); });

View File

@ -7,11 +7,13 @@ use core::{num::NonZeroU8, ops::Not};
use std::collections::HashMap; use std::collections::HashMap;
use leptos::prelude::*; use leptos::prelude::*;
use sorted_vec::SortedSet;
use werewolves_proto::{ use werewolves_proto::{
character::CharacterId, character::CharacterId,
game::{Category, GameSettings}, game::{Category, GameSettings},
message::{ message::{
CharacterIdentity, CharacterState, PlayerState, CharacterIdentity, CharacterState, PlayerState,
dead::DeadChatMessage,
host::{HostGameMessage, HostLobbyMessage, HostMessage, ServerToHostMessage as Srv2Host}, host::{HostGameMessage, HostLobbyMessage, HostMessage, ServerToHostMessage as Srv2Host},
night::{ActionPrompt, ActionResult}, night::{ActionPrompt, ActionResult},
}, },
@ -55,6 +57,7 @@ pub fn HostGamePage(
error: WriteSignal<Option<WolfError>>, error: WriteSignal<Option<WolfError>>,
message: Signal<Option<Srv2Host>>, message: Signal<Option<Srv2Host>>,
reply: WriteSignal<Option<HostMessage>>, reply: WriteSignal<Option<HostMessage>>,
dead_chat_messages: RwSignal<SortedSet<DeadChatMessage>>,
) -> impl IntoView { ) -> impl IntoView {
let prefs = expect_context::<(Signal<Preferences>, WriteSignal<Preferences>)>().0; let prefs = expect_context::<(Signal<Preferences>, WriteSignal<Preferences>)>().0;
let page = RwSignal::new(HostPage::default()); let page = RwSignal::new(HostPage::default());

View File

@ -161,10 +161,13 @@ fn DaytimePlayer(
.. ..
} = character; } = character;
let character_id = identity.character_id; let character_id = identity.character_id;
let dead = died_to.is_some();
let select = move |_| { let select = move |_| {
if !dead {
reply.set(Some(HostMessage::InGame(HostGameMessage::Day( reply.set(Some(HostMessage::InGame(HostGameMessage::Day(
HostDayMessage::MarkForExecution(character_id), HostDayMessage::MarkForExecution(character_id),
)))) ))))
}
}; };
let icon = role.icon().unwrap_or_else(|| role.alignment().icon()); let icon = role.icon().unwrap_or_else(|| role.alignment().icon());

View File

@ -39,6 +39,7 @@ pub fn PlayerGamePage(
message: Signal<Option<Srv2Client>>, message: Signal<Option<Srv2Client>>,
reply: WriteSignal<Option<ClientMessage>>, reply: WriteSignal<Option<ClientMessage>>,
disconnect: RwSignal<bool>, disconnect: RwSignal<bool>,
dead_chat_messages: RwSignal<SortedSet<DeadChatMessage>>,
) -> impl IntoView { ) -> impl IntoView {
let dead_chat: RwSignal<Option<SortedSet<DeadChatMessage>>> = RwSignal::new(None); let dead_chat: RwSignal<Option<SortedSet<DeadChatMessage>>> = RwSignal::new(None);
let page: RwSignal<Option<Page>> = RwSignal::new(None); let page: RwSignal<Option<Page>> = RwSignal::new(None);

View File

@ -360,7 +360,11 @@ pub fn TargetPicker(
}) })
.collect_view(); .collect_view();
view! { <div class="target-picker" class:allow-scroll=pick.is_some()>{targets}</div> } view! {
<div class="target-picker" class:allow-scroll=pick.is_some()>
{targets}
</div>
}
} }
#[component] #[component]

View File

@ -46,7 +46,7 @@ pub fn RoleResult(
.into_any(), .into_any(),
None => view! { <Cover message="go to sleep" /> }.into_any(), None => view! { <Cover message="go to sleep" /> }.into_any(),
}, },
ActionResult::ShiftFailed => view!{<ShiftFailed />}.into_any(), ActionResult::ShiftFailed => view! { <ShiftFailed /> }.into_any(),
ActionResult::SkippedByHost | ActionResult::SkippedByHost |
ActionResult::Continue => { ActionResult::Continue => {
Effect::new(move || { Effect::new(move || {

View File

@ -39,9 +39,12 @@ pub fn ArcanistResult(
targets: (PublicIdentity, PublicIdentity), targets: (PublicIdentity, PublicIdentity),
) -> impl IntoView { ) -> impl IntoView {
let text = match value { let text = match value {
AlignmentEq::Same => view! {"ARE THE SAME"}.into_any(), AlignmentEq::Same => view! { "ARE THE SAME" }.into_any(),
AlignmentEq::Different => { AlignmentEq::Different => {
view! {"ARE "<span class="wolves underline">"DIFFERENT"</span>}.into_any() view! {
"ARE "
<span class="wolves underline">"DIFFERENT"</span>
}.into_any()
} }
}; };
let icons = match value { let icons = match value {