fix: militia never being spent
This commit is contained in:
parent
8b894b4c8c
commit
fc4da3bcc5
|
|
@ -777,6 +777,28 @@ impl Character {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn militia<'a>(&'a self) -> Result<Militia<'a>> {
|
||||||
|
let title = self.role.title();
|
||||||
|
match &self.role {
|
||||||
|
Role::Militia { targeted } => Ok(Militia(targeted)),
|
||||||
|
_ => Err(GameError::InvalidRole {
|
||||||
|
expected: RoleTitle::Militia,
|
||||||
|
got: title,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn militia_mut<'a>(&'a mut self) -> Result<MilitiaMut<'a>> {
|
||||||
|
let title = self.role.title();
|
||||||
|
match &mut self.role {
|
||||||
|
Role::Militia { targeted } => Ok(MilitiaMut(targeted)),
|
||||||
|
_ => Err(GameError::InvalidRole {
|
||||||
|
expected: RoleTitle::Militia,
|
||||||
|
got: title,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn initial_shown_role(&self) -> RoleTitle {
|
pub const fn initial_shown_role(&self) -> RoleTitle {
|
||||||
self.role.initial_shown_role()
|
self.role.initial_shown_role()
|
||||||
}
|
}
|
||||||
|
|
@ -818,6 +840,7 @@ decl_ref_and_mut!(
|
||||||
BlackKnight, BlackKnightMut: Option<DiedTo>;
|
BlackKnight, BlackKnightMut: Option<DiedTo>;
|
||||||
Guardian, GuardianMut: Option<PreviousGuardianAction>;
|
Guardian, GuardianMut: Option<PreviousGuardianAction>;
|
||||||
Direwolf, DirewolfMut: Option<CharacterId>;
|
Direwolf, DirewolfMut: Option<CharacterId>;
|
||||||
|
Militia, MilitiaMut: Option<CharacterId>;
|
||||||
);
|
);
|
||||||
|
|
||||||
pub struct BlackKnightKill<'a> {
|
pub struct BlackKnightKill<'a> {
|
||||||
|
|
|
||||||
|
|
@ -95,4 +95,6 @@ pub enum GameError {
|
||||||
MissingTime(GameTime),
|
MissingTime(GameTime),
|
||||||
#[error("no previous during day")]
|
#[error("no previous during day")]
|
||||||
NoPreviousDuringDay,
|
NoPreviousDuringDay,
|
||||||
|
#[error("militia already spent")]
|
||||||
|
MilitiaSpent,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,19 @@ impl KillOutcome {
|
||||||
pub fn apply_to_village(self, village: &mut Village) -> Result<()> {
|
pub fn apply_to_village(self, village: &mut Village) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
KillOutcome::Single(character_id, died_to) => {
|
KillOutcome::Single(character_id, died_to) => {
|
||||||
village.character_by_id_mut(character_id)?.kill(died_to);
|
village
|
||||||
|
.character_by_id_mut(character_id)?
|
||||||
|
.kill(died_to.clone());
|
||||||
|
if let DiedTo::Militia { killer, .. } = died_to
|
||||||
|
&& let Some(existing) = village
|
||||||
|
.character_by_id_mut(killer)?
|
||||||
|
.militia_mut()?
|
||||||
|
.replace(character_id)
|
||||||
|
{
|
||||||
|
log::error!("militia kill after already recording a kill on {existing}");
|
||||||
|
return Err(GameError::MilitiaSpent);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
KillOutcome::Guarding {
|
KillOutcome::Guarding {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
use core::{num::NonZeroU8, ops::Deref};
|
||||||
|
#[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},
|
||||||
|
message::night::ActionPromptTitle,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn spent_shot() {
|
||||||
|
let players = gen_players(1..10);
|
||||||
|
let militia = players[0].player_id;
|
||||||
|
let target_wolf = players[1].player_id;
|
||||||
|
let other_wolf = players[2].player_id;
|
||||||
|
|
||||||
|
let mut settings = GameSettings::empty();
|
||||||
|
settings.add_and_assign(SetupRole::Militia, militia);
|
||||||
|
settings.add_and_assign(SetupRole::Werewolf, target_wolf);
|
||||||
|
settings.add_and_assign(SetupRole::Werewolf, other_wolf);
|
||||||
|
|
||||||
|
settings.fill_remaining_slots_with_villagers(9);
|
||||||
|
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.execute().title().wolf_pack_kill();
|
||||||
|
game.mark(game.living_villager().character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().militia();
|
||||||
|
game.mark(game.character_by_player_id(target_wolf).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
game.character_by_player_id(militia)
|
||||||
|
.militia()
|
||||||
|
.unwrap()
|
||||||
|
.deref()
|
||||||
|
.clone(),
|
||||||
|
Some(game.character_by_player_id(target_wolf).character_id())
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
game.character_by_player_id(target_wolf).died_to().cloned(),
|
||||||
|
Some(DiedTo::Militia {
|
||||||
|
killer: game.character_by_player_id(militia).character_id(),
|
||||||
|
night: NonZeroU8::new(1).unwrap()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
game.execute().title().wolf_pack_kill();
|
||||||
|
game.mark(game.living_villager().character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next_expect_day();
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ mod hunter;
|
||||||
mod insomniac;
|
mod insomniac;
|
||||||
mod lone_wolf;
|
mod lone_wolf;
|
||||||
mod mason;
|
mod mason;
|
||||||
|
mod militia;
|
||||||
mod mortician;
|
mod mortician;
|
||||||
mod pyremaster;
|
mod pyremaster;
|
||||||
mod scapegoat;
|
mod scapegoat;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue