beholder target roleblocked sees nothing

slightly nicer game end screen
This commit is contained in:
emilis 2025-11-08 00:44:55 +00:00
parent a2013adea9
commit ad29c3d59c
No known key found for this signature in database
11 changed files with 131 additions and 22 deletions

View File

@ -282,6 +282,15 @@ pub enum GameOver {
WolvesWin,
}
impl Display for GameOver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GameOver::VillageWins => f.write_str("village wins"),
GameOver::WolvesWin => f.write_str("wolves win"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Pool<T>
where

View File

@ -932,7 +932,7 @@ impl Night {
NightState::Active {
current_result: CurrentResult::Result(current_result),
..
} => Some(&current_result),
} => Some(current_result),
NightState::Active {
current_result: CurrentResult::GoBackToSleepAfterShown { .. },
..

View File

@ -395,10 +395,13 @@ impl Night {
if let Some(result) = self.used_actions.iter().find_map(|(prompt, result, _)| {
prompt.matches_beholding(*marked).then_some(result)
}) && self.dies_tonight(*marked)?
&& !matches!(result, ActionResult::RoleBlocked)
{
Ok(ActionComplete {
result: result.clone(),
result: if matches!(result, ActionResult::RoleBlocked) {
ActionResult::BeholderSawNothing
} else {
result.clone()
},
change: None,
}
.into())

View File

@ -83,11 +83,13 @@ pub enum StoryActionResult {
Mortician(DiedToTitle),
Insomniac { visits: Box<[CharacterId]> },
Empath { scapegoat: bool },
BeholderSawNothing,
}
impl StoryActionResult {
pub fn new(result: ActionResult) -> Option<Self> {
Some(match result {
ActionResult::BeholderSawNothing => Self::BeholderSawNothing,
ActionResult::RoleBlocked => Self::RoleBlocked,
ActionResult::Seer(alignment) => Self::Seer(alignment),
ActionResult::PowerSeer { powerful } => Self::PowerSeer { powerful },

View File

@ -484,6 +484,7 @@ pub enum ActionResult {
Mortician(DiedToTitle),
Insomniac(Visits),
Empath { scapegoat: bool },
BeholderSawNothing,
GoBackToSleep,
Continue,
}

View File

@ -1306,7 +1306,10 @@ input {
margin-top: 2%;
font-size: 1.5vw;
height: 100vh;
.setup {
height: 85%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
@ -1322,6 +1325,7 @@ input {
&.final {
margin-top: 1cm;
margin-bottom: 1cm;
}
& .title {
@ -1870,3 +1874,26 @@ input {
font-size: 3rem;
text-align: center;
}
.victory {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
font-size: 3vw;
height: max-content;
gap: 1cm;
padding: 1cm;
}
.end-screen {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
width: 100vw;
height: 100vh;
align-items: center;
justify-content: center;
}

View File

@ -41,7 +41,7 @@ use yew::{html::Scope, prelude::*};
use crate::{
callback,
components::{
Button, CoverOfDarkness, Lobby, LobbyPlayerAction, RoleReveal, Settings, Story,
Button, CoverOfDarkness, Lobby, LobbyPlayerAction, RoleReveal, Settings, Story, Victory,
action::{ActionResultView, Prompt},
host::{DaytimePlayerList, Setup},
},
@ -312,6 +312,16 @@ impl Component for Host {
log::trace!("state: {:?}", self.state);
let content = match self.state.clone() {
HostState::Story { story, page } => {
if let Some(outcome) = story
.final_village()
.ok()
.and_then(|village| village.is_game_over())
&& self.big_screen
{
html! {
<Victory outcome={outcome}/>
}
} else {
let new_lobby_click = crate::callback::send_message(
HostMessage::PostGame(PostGameMessage::NewLobby),
self.send.clone(),
@ -323,6 +333,7 @@ impl Component for Host {
</div>
}
}
}
HostState::GameOver { result } => {
let cont = self.big_screen.not().then(|| {
crate::callback::send_message(
@ -332,15 +343,7 @@ impl Component for Host {
});
html! {
<CoverOfDarkness
message={match result {
GameOver::VillageWins => "village wins",
GameOver::WolvesWin => "wolves win",
}}
next={cont}
>
{"continue"}
</CoverOfDarkness>
<Victory outcome={result} cont={cont}/>
}
}
HostState::Disconnected => html! {

View File

@ -25,8 +25,8 @@ use yew::prelude::*;
use crate::{
components::{Button, CoverOfDarkness, Icon, IconSource, Identity},
pages::{
AdjudicatorResult, ArcanistResult, EmpathResult, GravediggerResultPage, InsomniacResult,
MorticianResultPage, PowerSeerResult, RoleblockPage, SeerResult,
AdjudicatorResult, ArcanistResult, BeholderSawNothing, EmpathResult, GravediggerResultPage,
InsomniacResult, MorticianResultPage, PowerSeerResult, RoleblockPage, SeerResult,
},
};
@ -58,6 +58,9 @@ pub fn ActionResultView(props: &ActionResultProps) -> Html {
.not()
.then(|| html! {<Button on_click={on_continue}>{"continue"}</Button>});
let body = match &props.result {
ActionResult::BeholderSawNothing => html! {
<BeholderSawNothing />
},
ActionResult::PowerSeer { powerful } => {
html! {
<PowerSeerResult powerful={*powerful}/>

View File

@ -274,6 +274,9 @@ struct StoryNightResultProps {
#[function_component]
fn StoryNightResult(StoryNightResultProps { result, characters }: &StoryNightResultProps) -> Html {
match result {
StoryActionResult::BeholderSawNothing => html!{
<span>{"but saw nothing"}</span>
},
StoryActionResult::RoleBlocked => html! {
<span>{"but was role blocked"}</span>
},

View File

@ -0,0 +1,42 @@
use werewolves_proto::game::GameOver;
use yew::prelude::*;
use crate::components::{Button, Icon, IconSource, IconType};
#[derive(Debug, Clone, PartialEq, Properties)]
pub struct VictoryProps {
pub outcome: GameOver,
#[prop_or_default]
pub cont: Option<Callback<()>>,
}
#[function_component]
pub fn Victory(VictoryProps { outcome, cont }: &VictoryProps) -> Html {
let cont_btn = cont.clone().map(|cont| {
html! {
<Button on_click={cont}>{"continue"}</Button>
}
});
match outcome {
GameOver::WolvesWin => html! {
<div class="end-screen">
<div class="victory wolves faint">
<Icon source={IconSource::Wolves} icon_type={IconType::Informational}/>
<h1>{"Wolves Win"}</h1>
<Icon source={IconSource::Wolves} icon_type={IconType::Informational}/>
</div>
{cont_btn}
</div>
},
GameOver::VillageWins => html! {
<div class="end-screen">
<div class="victory village faint">
<Icon source={IconSource::Village} icon_type={IconType::Informational}/>
<h1>{"Village Wins"}</h1>
<Icon source={IconSource::Village} icon_type={IconType::Informational}/>
</div>
{cont_btn}
</div>
},
}
}

View File

@ -14,6 +14,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use yew::prelude::*;
use crate::components::{Icon, IconSource, IconType};
#[function_component]
pub fn BeholderPage1() -> Html {
html! {
@ -27,3 +29,17 @@ pub fn BeholderPage1() -> Html {
</div>
}
}
#[function_component]
pub fn BeholderSawNothing() -> Html {
html! {
<div class="role-page">
<h1 class="intel">{"BEHOLDER"}</h1>
<div class="information intel faint">
<h1>{"YOUR TARGET HAS DIED"}</h1>
<h1><Icon source={IconSource::RedX} icon_type={IconType::Informational}/></h1>
<h1>{"BUT SAW NOTHING"}</h1>
</div>
</div>
}
}