2025-11-05 20:24:51 +00:00
|
|
|
// 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/>.
|
2025-10-12 23:48:52 +01:00
|
|
|
use core::num::NonZeroU8;
|
|
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
|
diedto::DiedTo,
|
|
|
|
|
error::GameError,
|
2025-10-17 21:16:10 +01:00
|
|
|
game::night::{
|
|
|
|
|
ActionComplete, CurrentResult, Night, NightState, ResponseOutcome, changes::NightChange,
|
|
|
|
|
},
|
2025-10-12 23:48:52 +01:00
|
|
|
message::night::{ActionPrompt, ActionResponse, ActionResult},
|
|
|
|
|
player::Protection,
|
|
|
|
|
role::{AlignmentEq, PreviousGuardianAction, RoleBlock, RoleTitle},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type Result<T> = core::result::Result<T, GameError>;
|
|
|
|
|
|
|
|
|
|
impl Night {
|
|
|
|
|
pub(super) fn process(&self, resp: ActionResponse) -> Result<ResponseOutcome> {
|
|
|
|
|
let current_prompt = match &self.night_state {
|
|
|
|
|
NightState::Active {
|
2025-10-17 21:16:10 +01:00
|
|
|
current_result: CurrentResult::GoBackToSleepAfterShown { .. },
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
| NightState::Active {
|
|
|
|
|
current_result: CurrentResult::Result(ActionResult::GoBackToSleep),
|
2025-10-12 23:48:52 +01:00
|
|
|
..
|
|
|
|
|
} => return Err(GameError::NightNeedsNext),
|
|
|
|
|
NightState::Active {
|
|
|
|
|
current_prompt,
|
2025-10-17 21:16:10 +01:00
|
|
|
current_result: CurrentResult::None,
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
| NightState::Active {
|
|
|
|
|
current_prompt,
|
|
|
|
|
current_result: CurrentResult::Result(_),
|
2025-10-12 23:48:52 +01:00
|
|
|
..
|
|
|
|
|
} => current_prompt,
|
|
|
|
|
NightState::Complete => return Err(GameError::NightOver),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match resp {
|
|
|
|
|
ActionResponse::MarkTarget(mark) => {
|
|
|
|
|
return Ok(ResponseOutcome::PromptUpdate(
|
|
|
|
|
current_prompt.with_mark(mark)?,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
ActionResponse::Shapeshift => {
|
|
|
|
|
return match current_prompt {
|
|
|
|
|
ActionPrompt::Shapeshifter {
|
|
|
|
|
character_id: source,
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Shapeshift {
|
|
|
|
|
source: source.character_id,
|
|
|
|
|
into: self
|
|
|
|
|
.changes_from_actions()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.find_map(|c| match c {
|
|
|
|
|
NightChange::Kill {
|
|
|
|
|
target,
|
|
|
|
|
died_to: DiedTo::Wolfpack { .. },
|
|
|
|
|
} => Some(target),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
.ok_or(GameError::InvalidTarget)?,
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
_ => Err(GameError::InvalidMessageForGameState),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
ActionResponse::Continue => {
|
|
|
|
|
if let ActionPrompt::Insomniac { character_id } = current_prompt {
|
|
|
|
|
return Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::Insomniac(
|
|
|
|
|
self.get_visits_for(character_id.character_id),
|
|
|
|
|
),
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into());
|
|
|
|
|
}
|
|
|
|
|
if let ActionPrompt::RoleChange {
|
|
|
|
|
character_id,
|
|
|
|
|
new_role,
|
|
|
|
|
} = current_prompt
|
|
|
|
|
{
|
|
|
|
|
return Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::RoleChange(
|
|
|
|
|
character_id.character_id,
|
|
|
|
|
*new_role,
|
|
|
|
|
)),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match current_prompt {
|
|
|
|
|
ActionPrompt::LoneWolfKill {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Kill {
|
|
|
|
|
target: *marked,
|
|
|
|
|
died_to: DiedTo::LoneWolf {
|
|
|
|
|
killer: character_id.character_id,
|
|
|
|
|
night: self.night,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::RoleChange { .. }
|
|
|
|
|
| ActionPrompt::WolvesIntro { .. }
|
|
|
|
|
| ActionPrompt::CoverOfDarkness => {
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: None,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::ElderReveal { character_id } => {
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::ElderReveal {
|
|
|
|
|
elder: character_id.character_id,
|
|
|
|
|
}),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Seer {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
let alignment = self.village.character_by_id(*marked)?.alignment();
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::Seer(alignment),
|
|
|
|
|
change: None,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Protector {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
character_id,
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Protection {
|
|
|
|
|
target: *marked,
|
|
|
|
|
protection: Protection::Protector {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::Arcanist {
|
|
|
|
|
marked: (Some(marked1), Some(marked2)),
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
let same = self.village.character_by_id(*marked1)?.alignment()
|
|
|
|
|
== self.village.character_by_id(*marked2)?.alignment();
|
|
|
|
|
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::Arcanist(AlignmentEq::new(same)),
|
|
|
|
|
change: None,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Gravedigger {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
let dig_role = self.village.character_by_id(*marked)?.gravedigger_dig();
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GraveDigger(dig_role),
|
|
|
|
|
change: None,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Hunter {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::HunterTarget {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
target: *marked,
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::Militia {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Kill {
|
|
|
|
|
target: *marked,
|
|
|
|
|
died_to: DiedTo::Militia {
|
|
|
|
|
killer: character_id.character_id,
|
|
|
|
|
night: NonZeroU8::new(self.night)
|
|
|
|
|
.ok_or(GameError::InvalidMessageForGameState)?,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::Militia { marked: None, .. } => {
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(Default::default()))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::MapleWolf {
|
|
|
|
|
character_id,
|
|
|
|
|
kill_or_die,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Kill {
|
|
|
|
|
target: *marked,
|
|
|
|
|
died_to: DiedTo::MapleWolf {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
night: NonZeroU8::new(self.night)
|
|
|
|
|
.ok_or(GameError::InvalidMessageForGameState)?,
|
|
|
|
|
starves_if_fails: *kill_or_die,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::MapleWolf { marked: None, .. } => {
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(Default::default()))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Guardian {
|
|
|
|
|
character_id,
|
|
|
|
|
previous: None,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Protection {
|
|
|
|
|
target: *marked,
|
|
|
|
|
protection: Protection::Guardian {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
guarding: false,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::Guardian {
|
|
|
|
|
character_id,
|
|
|
|
|
previous: Some(PreviousGuardianAction::Guard(prev_target)),
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
if prev_target.character_id == *marked {
|
|
|
|
|
return Err(GameError::InvalidTarget);
|
|
|
|
|
}
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Protection {
|
|
|
|
|
target: *marked,
|
|
|
|
|
protection: Protection::Guardian {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
guarding: false,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Guardian {
|
|
|
|
|
character_id,
|
|
|
|
|
previous: Some(PreviousGuardianAction::Protect(prev_protect)),
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Protection {
|
|
|
|
|
target: *marked,
|
|
|
|
|
protection: Protection::Guardian {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
guarding: prev_protect.character_id == *marked,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::WolfPackKill {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Kill {
|
|
|
|
|
target: *marked,
|
|
|
|
|
died_to: DiedTo::Wolfpack {
|
|
|
|
|
killing_wolf: self
|
|
|
|
|
.village
|
|
|
|
|
.killing_wolf()
|
|
|
|
|
.ok_or(GameError::NoWolves)?
|
|
|
|
|
.character_id(),
|
|
|
|
|
night: NonZeroU8::new(self.night)
|
|
|
|
|
.ok_or(GameError::InvalidMessageForGameState)?,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::Shapeshifter { character_id } => {
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: match &resp {
|
|
|
|
|
ActionResponse::Continue => None,
|
|
|
|
|
ActionResponse::Shapeshift => Some(NightChange::Shapeshift {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
into: self
|
|
|
|
|
.changes_from_actions()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.find_map(|c| match c {
|
|
|
|
|
NightChange::Kill {
|
|
|
|
|
target,
|
|
|
|
|
died_to: DiedTo::Wolfpack { .. },
|
|
|
|
|
} => Some(target),
|
|
|
|
|
_ => None,
|
|
|
|
|
})
|
|
|
|
|
.ok_or(GameError::InvalidTarget)?,
|
|
|
|
|
}),
|
|
|
|
|
_ => return Err(GameError::InvalidMessageForGameState),
|
|
|
|
|
},
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::AlphaWolf {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Kill {
|
|
|
|
|
target: *marked,
|
|
|
|
|
died_to: DiedTo::AlphaWolf {
|
|
|
|
|
killer: character_id.character_id,
|
|
|
|
|
night: NonZeroU8::new(self.night)
|
|
|
|
|
.ok_or(GameError::InvalidMessageForGameState)?,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::AlphaWolf { marked: None, .. } => {
|
|
|
|
|
Ok(ResponseOutcome::ActionComplete(Default::default()))
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::DireWolf {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ResponseOutcome::ActionComplete(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::RoleBlock {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
target: *marked,
|
|
|
|
|
block_type: RoleBlock::Direwolf,
|
|
|
|
|
}),
|
|
|
|
|
})),
|
|
|
|
|
ActionPrompt::Adjudicator {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::Adjudicator {
|
|
|
|
|
killer: self.village.character_by_id(*marked)?.killer(),
|
|
|
|
|
},
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::PowerSeer {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::PowerSeer {
|
|
|
|
|
powerful: self.village.character_by_id(*marked)?.powerful(),
|
|
|
|
|
},
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::Mortician {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::Mortician(
|
|
|
|
|
self.village
|
|
|
|
|
.character_by_id(*marked)?
|
|
|
|
|
.died_to()
|
|
|
|
|
.ok_or(GameError::InvalidTarget)?
|
|
|
|
|
.title(),
|
|
|
|
|
),
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::Beholder {
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
if let Some(result) = self.used_actions.iter().find_map(|(prompt, result, _)| {
|
|
|
|
|
prompt.matches_beholding(*marked).then_some(result)
|
2025-11-07 23:50:45 +00:00
|
|
|
}) && self.dies_tonight(*marked)?
|
|
|
|
|
{
|
2025-10-12 23:48:52 +01:00
|
|
|
Ok(ActionComplete {
|
2025-11-08 00:44:55 +00:00
|
|
|
result: if matches!(result, ActionResult::RoleBlocked) {
|
|
|
|
|
ActionResult::BeholderSawNothing
|
|
|
|
|
} else {
|
|
|
|
|
result.clone()
|
|
|
|
|
},
|
2025-10-12 23:48:52 +01:00
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into())
|
|
|
|
|
} else {
|
|
|
|
|
Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::MasonsWake { .. } => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::MasonLeaderRecruit {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::Continue,
|
|
|
|
|
change: Some(NightChange::MasonRecruit {
|
|
|
|
|
mason_leader: character_id.character_id,
|
|
|
|
|
recruiting: *marked,
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::Empath {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => {
|
|
|
|
|
let marked = self.village.character_by_id(*marked)?;
|
|
|
|
|
let scapegoat = marked.role_title() == RoleTitle::Scapegoat;
|
|
|
|
|
|
|
|
|
|
Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::Empath { scapegoat },
|
|
|
|
|
change: scapegoat.then(|| NightChange::EmpathFoundScapegoat {
|
|
|
|
|
empath: character_id.character_id,
|
|
|
|
|
scapegoat: marked.character_id(),
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
.into())
|
|
|
|
|
}
|
|
|
|
|
ActionPrompt::Vindicator {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: Some(NightChange::Protection {
|
|
|
|
|
target: *marked,
|
|
|
|
|
protection: Protection::Vindicator {
|
|
|
|
|
source: character_id.character_id,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::Insomniac { .. } => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
ActionPrompt::PyreMaster {
|
|
|
|
|
character_id,
|
|
|
|
|
marked: Some(marked),
|
|
|
|
|
..
|
|
|
|
|
} => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: NonZeroU8::new(self.night).map(|night| NightChange::Kill {
|
|
|
|
|
target: *marked,
|
|
|
|
|
died_to: DiedTo::PyreMaster {
|
|
|
|
|
killer: character_id.character_id,
|
|
|
|
|
night,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
|
|
|
|
|
ActionPrompt::PyreMaster { marked: None, .. }
|
|
|
|
|
| ActionPrompt::MasonLeaderRecruit { marked: None, .. } => Ok(ActionComplete {
|
|
|
|
|
result: ActionResult::GoBackToSleep,
|
|
|
|
|
change: None,
|
|
|
|
|
}
|
|
|
|
|
.into()),
|
|
|
|
|
|
|
|
|
|
ActionPrompt::Adjudicator { marked: None, .. }
|
|
|
|
|
| ActionPrompt::PowerSeer { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Mortician { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Beholder { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Empath { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Vindicator { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Protector { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Arcanist {
|
|
|
|
|
marked: (None, None),
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
| ActionPrompt::Arcanist {
|
|
|
|
|
marked: (None, Some(_)),
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
| ActionPrompt::Arcanist {
|
|
|
|
|
marked: (Some(_), None),
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
| ActionPrompt::LoneWolfKill { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Gravedigger { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Hunter { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Guardian { marked: None, .. }
|
|
|
|
|
| ActionPrompt::WolfPackKill { marked: None, .. }
|
|
|
|
|
| ActionPrompt::DireWolf { marked: None, .. }
|
|
|
|
|
| ActionPrompt::Seer { marked: None, .. } => Err(GameError::InvalidMessageForGameState),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|