From bc7cb5b2cbbaaf413c1fc57a1664285218e2dafa Mon Sep 17 00:00:00 2001 From: emilis Date: Wed, 19 Nov 2025 00:36:46 +0000 Subject: [PATCH] vindicator: activate on alignment, not team --- werewolves-proto/src/character.rs | 2 +- werewolves-proto/src/game_test/mod.rs | 4 + werewolves-proto/src/game_test/previous.rs | 4 + werewolves-proto/src/game_test/role/mod.rs | 1 + .../src/game_test/role/vindicator.rs | 109 ++++++++++++++++++ werewolves/src/clients/host/host.rs | 2 +- werewolves/src/main.rs | 5 + 7 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 werewolves-proto/src/game_test/role/vindicator.rs diff --git a/werewolves-proto/src/character.rs b/werewolves-proto/src/character.rs index 2b10931..a35365b 100644 --- a/werewolves-proto/src/character.rs +++ b/werewolves-proto/src/character.rs @@ -552,7 +552,7 @@ impl Character { && village .executions_on_day(last_day) .iter() - .any(|c| c.is_village()) + .any(|c| c.alignment().village()) { prompts.push(ActionPrompt::Vindicator { character_id: self.identity(), diff --git a/werewolves-proto/src/game_test/mod.rs b/werewolves-proto/src/game_test/mod.rs index 007f6d0..d163a54 100644 --- a/werewolves-proto/src/game_test/mod.rs +++ b/werewolves-proto/src/game_test/mod.rs @@ -934,6 +934,10 @@ fn big_game_test_based_on_story_test() { game.mark(protect.character_id()); game.r#continue().sleep(); + game.next().title().vindicator(); + game.mark_villager(); + game.r#continue().sleep(); + game.next().title().wolf_pack_kill(); game.mark(protect.character_id()); game.r#continue().r#continue(); diff --git a/werewolves-proto/src/game_test/previous.rs b/werewolves-proto/src/game_test/previous.rs index 9fb4e05..9fa8b9f 100644 --- a/werewolves-proto/src/game_test/previous.rs +++ b/werewolves-proto/src/game_test/previous.rs @@ -348,6 +348,10 @@ fn previous_prompt() { game.mark(protect.character_id()); game.r#continue().sleep(); + game.next().title().vindicator(); + game.mark_villager(); + game.r#continue().sleep(); + game.next().title().wolf_pack_kill(); game.mark(protect.character_id()); game.r#continue().r#continue(); diff --git a/werewolves-proto/src/game_test/role/mod.rs b/werewolves-proto/src/game_test/role/mod.rs index 1f33ccb..82e3772 100644 --- a/werewolves-proto/src/game_test/role/mod.rs +++ b/werewolves-proto/src/game_test/role/mod.rs @@ -32,4 +32,5 @@ mod protector; mod pyremaster; mod scapegoat; mod shapeshifter; +mod vindicator; mod weightlifter; diff --git a/werewolves-proto/src/game_test/role/vindicator.rs b/werewolves-proto/src/game_test/role/vindicator.rs new file mode 100644 index 0000000..c330514 --- /dev/null +++ b/werewolves-proto/src/game_test/role/vindicator.rs @@ -0,0 +1,109 @@ +// Copyright (C) 2025 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 . +use core::num::NonZeroU8; + +#[allow(unused)] +use pretty_assertions::{assert_eq, assert_ne, assert_str_eq}; + +use crate::{ + diedto::DiedTo, + game::{Game, GameSettings, SetupRole}, + game_test::{ + ActionPromptTitleExt, ActionResultExt, GameExt, SettingsExt, gen_players, init_log, + }, + message::night::ActionPromptTitle, +}; + +#[test] +fn direwolf_kill_activates() { + init_log(); + let players = gen_players(1..21); + let mut player_ids = players.iter().map(|p| p.player_id); + let vindicator = player_ids.next().unwrap(); + let wolf = player_ids.next().unwrap(); + let direwolf = player_ids.next().unwrap(); + let mut settings = GameSettings::empty(); + settings.add_and_assign(SetupRole::Werewolf, wolf); + settings.add_and_assign(SetupRole::DireWolf, direwolf); + settings.add_and_assign(SetupRole::Vindicator, vindicator); + settings.fill_remaining_slots_with_villagers(players.len()); + let mut game = Game::new(&players, settings).unwrap(); + game.r#continue().r#continue(); + assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro); + game.r#continue().r#continue(); + + game.next().title().direwolf(); + game.mark_villager(); + game.r#continue().sleep(); + + game.next_expect_day(); + game.mark_for_execution(game.character_by_player_id(direwolf).character_id()); + game.execute().title().vindicator(); + let prot = game.living_villager(); + game.mark(prot.character_id()); + game.r#continue().sleep(); + + game.next().title().wolf_pack_kill(); + game.mark(prot.character_id()); + game.r#continue().sleep(); + + game.next_expect_day(); + + assert_eq!( + game.character_by_player_id(prot.player_id()) + .died_to() + .cloned(), + None + ); +} + +#[test] +fn maplewolf_kill_does_not_activate() { + init_log(); + let players = gen_players(1..21); + let mut player_ids = players.iter().map(|p| p.player_id); + let vindicator = player_ids.next().unwrap(); + let wolf = player_ids.next().unwrap(); + let maplewolf = player_ids.next().unwrap(); + let mut settings = GameSettings::empty(); + settings.add_and_assign(SetupRole::Werewolf, wolf); + settings.add_and_assign(SetupRole::MapleWolf, maplewolf); + settings.add_and_assign(SetupRole::Vindicator, vindicator); + settings.fill_remaining_slots_with_villagers(players.len()); + let mut game = Game::new(&players, settings).unwrap(); + game.r#continue().r#continue(); + assert_eq!(game.next().title(), ActionPromptTitle::WolvesIntro); + game.r#continue().sleep(); + + game.next_expect_day(); + game.mark_for_execution(game.character_by_player_id(maplewolf).character_id()); + + game.execute().title().wolf_pack_kill(); + let target = game.living_villager(); + game.mark(target.character_id()); + game.r#continue().sleep(); + + game.next_expect_day(); + + assert_eq!( + game.character_by_player_id(target.player_id()) + .died_to() + .cloned(), + Some(DiedTo::Wolfpack { + killing_wolf: game.character_by_player_id(wolf).character_id(), + night: NonZeroU8::new(1).unwrap() + }) + ); +} diff --git a/werewolves/src/clients/host/host.rs b/werewolves/src/clients/host/host.rs index daf855b..49bc8eb 100644 --- a/werewolves/src/clients/host/host.rs +++ b/werewolves/src/clients/host/host.rs @@ -297,7 +297,7 @@ impl Component for Host { type Properties = (); fn create(ctx: &Context) -> Self { - gloo::utils::document().set_title("Werewolves Host"); + gloo::utils::document().set_title(format!("{} — host", crate::TITLE).as_str()); if let Some(clients) = gloo::utils::document() .query_selector("clients") .ok() diff --git a/werewolves/src/main.rs b/werewolves/src/main.rs index 64e3705..15b91a8 100644 --- a/werewolves/src/main.rs +++ b/werewolves/src/main.rs @@ -45,6 +45,10 @@ const BUILD_ID: &str = werewolves_macros::build_id!(); const BUILD_ID_LONG: &str = werewolves_macros::build_id_long!(); const BUILD_DIRTY: bool = werewolves_macros::build_dirty!(); const BUILD_TIME: &str = werewolves_macros::build_time!(); +const TITLE: &str = match option_env!("LOCAL") { + Some(_) => "LOCAL werewolves", + None => "werewolves", +}; use crate::clients::{ client::{Client2, ClientContext}, @@ -55,6 +59,7 @@ fn main() { wasm_logger::init(wasm_logger::Config::new(log::Level::Trace)); log::debug!("starting werewolves build {BUILD_ID}"); let document = gloo::utils::document(); + document.set_title(crate::TITLE); let url = document.document_uri().expect("get uri"); let url_obj = Url::new(&url).unwrap(); let path = url_obj.pathname();