apprentice now wakes in role-appropriate night order
This commit is contained in:
parent
082f0bba38
commit
8b894b4c8c
|
|
@ -195,6 +195,15 @@ impl Default for ActionComplete {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn night_sort_order(
|
||||||
|
left_prompt: &ActionPrompt,
|
||||||
|
right_prompt: &ActionPrompt,
|
||||||
|
) -> core::cmp::Ordering {
|
||||||
|
left_prompt
|
||||||
|
.partial_cmp(right_prompt)
|
||||||
|
.unwrap_or(core::cmp::Ordering::Equal)
|
||||||
|
}
|
||||||
|
|
||||||
enum Unless {
|
enum Unless {
|
||||||
TargetBlocked(CharacterId),
|
TargetBlocked(CharacterId),
|
||||||
TargetsBlocked(CharacterId, CharacterId),
|
TargetsBlocked(CharacterId, CharacterId),
|
||||||
|
|
@ -279,15 +288,14 @@ impl Night {
|
||||||
.flatten()
|
.flatten()
|
||||||
.chain(village.wolf_pack_kill())
|
.chain(village.wolf_pack_kill())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
action_queue.sort_by(|left_prompt, right_prompt| {
|
action_queue.sort_by(night_sort_order);
|
||||||
left_prompt
|
|
||||||
.partial_cmp(right_prompt)
|
|
||||||
.unwrap_or(core::cmp::Ordering::Equal)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut action_queue = VecDeque::from({
|
let mut action_queue = VecDeque::from({
|
||||||
// insert actions for role-changed roles
|
// insert actions for role-changed roles
|
||||||
let mut expanded_queue = Vec::new();
|
let mut expanded_queue = Vec::new();
|
||||||
|
let mut role_changes = Vec::new();
|
||||||
|
// here we replace the role change prompts with the prompt they *would* have gotten
|
||||||
|
// (if any). if they wouldn't get a prompt, just add the role change prompt back in
|
||||||
for action in action_queue {
|
for action in action_queue {
|
||||||
match &action {
|
match &action {
|
||||||
ActionPrompt::RoleChange {
|
ActionPrompt::RoleChange {
|
||||||
|
|
@ -296,8 +304,15 @@ impl Night {
|
||||||
} => {
|
} => {
|
||||||
let char = village.character_by_id(character_id.character_id)?;
|
let char = village.character_by_id(character_id.character_id)?;
|
||||||
let as_role = char.as_role(new_role.title_to_role_excl_apprentice());
|
let as_role = char.as_role(new_role.title_to_role_excl_apprentice());
|
||||||
|
let prompts = as_role.night_action_prompts(&village)?;
|
||||||
|
if prompts.is_empty() {
|
||||||
|
// they wouldn't get a prompt alongside the role change, so just add
|
||||||
|
// the role change prompt back in
|
||||||
expanded_queue.push(action);
|
expanded_queue.push(action);
|
||||||
for prompt in as_role.night_action_prompts(&village)? {
|
continue;
|
||||||
|
}
|
||||||
|
role_changes.push((character_id.character_id, *new_role));
|
||||||
|
for prompt in prompts {
|
||||||
expanded_queue.push(prompt);
|
expanded_queue.push(prompt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +322,23 @@ impl Night {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expanded_queue
|
expanded_queue.sort_by(night_sort_order);
|
||||||
|
let mut expanded_queue_with_role_changes =
|
||||||
|
Vec::with_capacity(expanded_queue.len() + role_changes.len());
|
||||||
|
for prompt in expanded_queue {
|
||||||
|
if let Some(char) = prompt.character_id()
|
||||||
|
&& let Some((_, role)) = role_changes.iter().find(|(c, _)| *c == char)
|
||||||
|
{
|
||||||
|
expanded_queue_with_role_changes.push(ActionPrompt::RoleChange {
|
||||||
|
character_id: village.character_by_id(char)?.identity(),
|
||||||
|
new_role: *role,
|
||||||
|
});
|
||||||
|
expanded_queue_with_role_changes.push(prompt);
|
||||||
|
} else {
|
||||||
|
expanded_queue_with_role_changes.push(prompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expanded_queue_with_role_changes
|
||||||
});
|
});
|
||||||
|
|
||||||
if night == 0 {
|
if night == 0 {
|
||||||
|
|
|
||||||
|
|
@ -699,18 +699,7 @@ fn yes_wolf_kill_n2() {
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
ServerToHostMessage::ActionResult(None, ActionResult::Continue)
|
||||||
);
|
);
|
||||||
|
game.next().title().wolf_pack_kill();
|
||||||
assert!(matches!(
|
|
||||||
game.process(HostGameMessage::Night(HostNightMessage::Next))
|
|
||||||
.unwrap(),
|
|
||||||
ServerToHostMessage::ActionPrompt(
|
|
||||||
ActionPrompt::WolfPackKill {
|
|
||||||
living_villagers: _,
|
|
||||||
marked: _,
|
|
||||||
},
|
|
||||||
0
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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;
|
||||||
|
#[allow(unused)]
|
||||||
|
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
diedto::DiedTo,
|
||||||
|
game::{Game, GameSettings, OrRandom, SetupRole},
|
||||||
|
game_test::{
|
||||||
|
ActionPromptTitleExt, ActionResultExt, AlignmentExt, GameExt, ServerToHostMessageExt,
|
||||||
|
SettingsExt, gen_players,
|
||||||
|
},
|
||||||
|
message::{
|
||||||
|
host::{HostDayMessage, HostGameMessage},
|
||||||
|
night::{ActionPrompt, ActionPromptTitle, ActionResult},
|
||||||
|
},
|
||||||
|
role::{Role, RoleTitle},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn beholder_appropriate_prompt_position() {
|
||||||
|
let players = gen_players(1..10);
|
||||||
|
let apprentice = players[0].player_id;
|
||||||
|
let beholder = players[1].player_id;
|
||||||
|
let wolf_player_id = players[2].player_id;
|
||||||
|
|
||||||
|
let mut settings = GameSettings::empty();
|
||||||
|
settings.add_and_assign(
|
||||||
|
SetupRole::Apprentice {
|
||||||
|
to: Some(RoleTitle::Beholder),
|
||||||
|
},
|
||||||
|
apprentice,
|
||||||
|
);
|
||||||
|
settings.add_and_assign(SetupRole::Beholder, beholder);
|
||||||
|
settings.add_and_assign(SetupRole::Werewolf, wolf_player_id);
|
||||||
|
|
||||||
|
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.character_by_player_id(beholder).character_id());
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
|
game.next().title().beholder();
|
||||||
|
game.mark(game.character_by_player_id(wolf_player_id).character_id());
|
||||||
|
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();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
game.next(),
|
||||||
|
ActionPrompt::RoleChange {
|
||||||
|
character_id: game.character_by_player_id(apprentice).identity(),
|
||||||
|
new_role: RoleTitle::Beholder
|
||||||
|
}
|
||||||
|
);
|
||||||
|
game.r#continue().r#continue();
|
||||||
|
game.next().title().beholder();
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
mod apprentice;
|
||||||
mod beholder;
|
mod beholder;
|
||||||
mod black_knight;
|
mod black_knight;
|
||||||
mod diseased;
|
mod diseased;
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,20 @@ fn redeemed_scapegoat_role_changes() {
|
||||||
night: NonZero::new(1).unwrap()
|
night: NonZero::new(1).unwrap()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(game.execute().title(), ActionPromptTitle::WolfPackKill);
|
||||||
|
let wolf_target_2 = game
|
||||||
|
.village()
|
||||||
|
.characters()
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.player_id() == wolf_target_2_player_id)
|
||||||
|
.unwrap()
|
||||||
|
.character_id();
|
||||||
|
game.mark_and_check(wolf_target_2);
|
||||||
|
game.r#continue().sleep();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
game.execute(),
|
game.next(),
|
||||||
ActionPrompt::RoleChange {
|
ActionPrompt::RoleChange {
|
||||||
character_id: game.character_by_player_id(scapegoat_player_id).identity(),
|
character_id: game.character_by_player_id(scapegoat_player_id).identity(),
|
||||||
new_role: RoleTitle::Seer
|
new_role: RoleTitle::Seer
|
||||||
|
|
@ -131,17 +143,6 @@ fn redeemed_scapegoat_role_changes() {
|
||||||
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
assert_eq!(game.r#continue().seer(), Alignment::Wolves);
|
||||||
game.r#continue().sleep();
|
game.r#continue().sleep();
|
||||||
|
|
||||||
assert_eq!(game.next().title(), ActionPromptTitle::WolfPackKill);
|
|
||||||
let wolf_target_2 = game
|
|
||||||
.village()
|
|
||||||
.characters()
|
|
||||||
.iter()
|
|
||||||
.find(|c| c.player_id() == wolf_target_2_player_id)
|
|
||||||
.unwrap()
|
|
||||||
.character_id();
|
|
||||||
game.mark_and_check(wolf_target_2);
|
|
||||||
game.r#continue().sleep();
|
|
||||||
|
|
||||||
game.next_expect_day();
|
game.next_expect_day();
|
||||||
|
|
||||||
let scapegoat = game.character_by_player_id(scapegoat_player_id);
|
let scapegoat = game.character_by_player_id(scapegoat_player_id);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue