293 lines
8.5 KiB
Rust
293 lines
8.5 KiB
Rust
|
|
use core::{
|
||
|
|
num::NonZeroU8,
|
||
|
|
ops::{Deref, Not},
|
||
|
|
};
|
||
|
|
|
||
|
|
use super::Result;
|
||
|
|
use crate::{
|
||
|
|
diedto::DiedTo,
|
||
|
|
error::GameError,
|
||
|
|
game::{Village, kill::taken::Taken, night::NightChange},
|
||
|
|
player::{CharacterId, Protection},
|
||
|
|
};
|
||
|
|
|
||
|
|
#[derive(Debug, PartialEq)]
|
||
|
|
pub enum KillOutcome {
|
||
|
|
Single(CharacterId, DiedTo),
|
||
|
|
Guarding {
|
||
|
|
original_killer: CharacterId,
|
||
|
|
original_target: CharacterId,
|
||
|
|
original_kill: DiedTo,
|
||
|
|
guardian: CharacterId,
|
||
|
|
night: NonZeroU8,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
impl KillOutcome {
|
||
|
|
pub fn apply_to_village(self, village: &mut Village) -> Result<()> {
|
||
|
|
match self {
|
||
|
|
KillOutcome::Single(character_id, died_to) => Ok(village
|
||
|
|
.character_by_id_mut(&character_id)
|
||
|
|
.ok_or(GameError::InvalidTarget)?
|
||
|
|
.kill(died_to)),
|
||
|
|
KillOutcome::Guarding {
|
||
|
|
original_killer,
|
||
|
|
original_target,
|
||
|
|
original_kill,
|
||
|
|
guardian,
|
||
|
|
night,
|
||
|
|
} => {
|
||
|
|
// check if guardian exists before we mutably borrow killer, which would
|
||
|
|
// prevent us from borrowing village to check after.
|
||
|
|
village
|
||
|
|
.character_by_id(&guardian)
|
||
|
|
.ok_or(GameError::InvalidTarget)?;
|
||
|
|
village
|
||
|
|
.character_by_id_mut(&original_killer)
|
||
|
|
.ok_or(GameError::InvalidTarget)?
|
||
|
|
.kill(DiedTo::GuardianProtecting {
|
||
|
|
night,
|
||
|
|
source: guardian.clone(),
|
||
|
|
protecting: original_target,
|
||
|
|
protecting_from: original_killer,
|
||
|
|
protecting_from_cause: Box::new(original_kill.clone()),
|
||
|
|
});
|
||
|
|
village
|
||
|
|
.character_by_id_mut(&guardian)
|
||
|
|
.ok_or(GameError::InvalidTarget)?
|
||
|
|
.kill(original_kill);
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn resolve_protection(
|
||
|
|
killer: CharacterId,
|
||
|
|
killed_with: &DiedTo,
|
||
|
|
target: &CharacterId,
|
||
|
|
protection: &Protection,
|
||
|
|
night: NonZeroU8,
|
||
|
|
) -> Option<KillOutcome> {
|
||
|
|
match protection {
|
||
|
|
Protection::Guardian {
|
||
|
|
source,
|
||
|
|
guarding: true,
|
||
|
|
} => Some(KillOutcome::Guarding {
|
||
|
|
original_killer: killer,
|
||
|
|
guardian: source.clone(),
|
||
|
|
original_target: target.clone(),
|
||
|
|
original_kill: killed_with.clone(),
|
||
|
|
night,
|
||
|
|
}),
|
||
|
|
Protection::Guardian {
|
||
|
|
source: _,
|
||
|
|
guarding: false,
|
||
|
|
}
|
||
|
|
| Protection::Protector { source: _ } => None,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn resolve_kill(
|
||
|
|
changes: &mut ChangesLookup<'_>,
|
||
|
|
target: &CharacterId,
|
||
|
|
died_to: &DiedTo,
|
||
|
|
night: u8,
|
||
|
|
village: &Village,
|
||
|
|
) -> Result<Option<KillOutcome>> {
|
||
|
|
if let DiedTo::MapleWolf {
|
||
|
|
source,
|
||
|
|
night,
|
||
|
|
starves_if_fails: true,
|
||
|
|
} = died_to
|
||
|
|
&& let Some(protection) = changes.protected_take(target)
|
||
|
|
{
|
||
|
|
return Ok(Some(
|
||
|
|
resolve_protection(source.clone(), died_to, target, &protection, *night).unwrap_or(
|
||
|
|
KillOutcome::Single(source.clone(), DiedTo::MapleWolfStarved { night: *night }),
|
||
|
|
),
|
||
|
|
));
|
||
|
|
}
|
||
|
|
if let DiedTo::Wolfpack {
|
||
|
|
night,
|
||
|
|
killing_wolf,
|
||
|
|
} = died_to
|
||
|
|
&& let Some(ss_source) = changes.shapeshifter()
|
||
|
|
{
|
||
|
|
let killing_wolf = village
|
||
|
|
.character_by_id(killing_wolf)
|
||
|
|
.ok_or(GameError::InvalidTarget)?;
|
||
|
|
|
||
|
|
match changes.protected_take(target) {
|
||
|
|
Some(protection) => {
|
||
|
|
return Ok(resolve_protection(
|
||
|
|
killing_wolf.character_id().clone(),
|
||
|
|
died_to,
|
||
|
|
target,
|
||
|
|
&protection,
|
||
|
|
*night,
|
||
|
|
));
|
||
|
|
}
|
||
|
|
None => {
|
||
|
|
// Wolf kill went through -- can kill shifter
|
||
|
|
return Ok(Some(KillOutcome::Single(
|
||
|
|
ss_source.clone(),
|
||
|
|
DiedTo::Shapeshift {
|
||
|
|
into: target.clone(),
|
||
|
|
night: *night,
|
||
|
|
},
|
||
|
|
)));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
let protection = match changes.protected_take(target) {
|
||
|
|
Some(prot) => prot,
|
||
|
|
None => return Ok(Some(KillOutcome::Single(target.clone(), died_to.clone()))),
|
||
|
|
};
|
||
|
|
|
||
|
|
match protection.deref() {
|
||
|
|
Protection::Guardian {
|
||
|
|
source,
|
||
|
|
guarding: true,
|
||
|
|
} => Ok(Some(KillOutcome::Guarding {
|
||
|
|
original_killer: died_to
|
||
|
|
.killer()
|
||
|
|
.ok_or(GameError::GuardianInvalidOriginalKill)?
|
||
|
|
.clone(),
|
||
|
|
original_target: target.clone(),
|
||
|
|
original_kill: died_to.clone(),
|
||
|
|
guardian: source.clone(),
|
||
|
|
night: NonZeroU8::new(night).unwrap(),
|
||
|
|
})),
|
||
|
|
Protection::Guardian {
|
||
|
|
source: _,
|
||
|
|
guarding: false,
|
||
|
|
}
|
||
|
|
| Protection::Protector { source: _ } => Ok(None),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub struct ChangesLookup<'a>(&'a [NightChange], Vec<usize>);
|
||
|
|
|
||
|
|
impl<'a> ChangesLookup<'a> {
|
||
|
|
pub fn new(changes: &'a [NightChange]) -> Self {
|
||
|
|
Self(changes, Vec::new())
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn killed(&self, target: &CharacterId) -> Option<&'a DiedTo> {
|
||
|
|
self.0.iter().enumerate().find_map(|(idx, c)| {
|
||
|
|
self.1
|
||
|
|
.contains(&idx)
|
||
|
|
.not()
|
||
|
|
.then(|| match c {
|
||
|
|
NightChange::Kill { target: t, died_to } => (t == target).then_some(died_to),
|
||
|
|
_ => None,
|
||
|
|
})
|
||
|
|
.flatten()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn release<T>(&mut self, taken: Taken<'a, T>) {
|
||
|
|
self.1.swap_remove(
|
||
|
|
self.1
|
||
|
|
.iter()
|
||
|
|
.enumerate()
|
||
|
|
.find_map(|(idx, c)| (*c == taken.idx()).then_some(idx))
|
||
|
|
.unwrap(),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn protected_take(&mut self, target: &CharacterId) -> Option<Taken<'a, Protection>> {
|
||
|
|
if let Some((idx, c)) = self.0.iter().enumerate().find_map(|(idx, c)| {
|
||
|
|
self.1
|
||
|
|
.contains(&idx)
|
||
|
|
.not()
|
||
|
|
.then(|| match c {
|
||
|
|
NightChange::Protection {
|
||
|
|
target: t,
|
||
|
|
protection,
|
||
|
|
} => (t == target).then_some((idx, protection)),
|
||
|
|
_ => None,
|
||
|
|
})
|
||
|
|
.flatten()
|
||
|
|
}) {
|
||
|
|
self.1.push(idx);
|
||
|
|
Some(Taken::new(idx, c))
|
||
|
|
} else {
|
||
|
|
None
|
||
|
|
}
|
||
|
|
}
|
||
|
|
pub fn protected(&self, target: &CharacterId) -> Option<&'a Protection> {
|
||
|
|
self.0.iter().enumerate().find_map(|(idx, c)| {
|
||
|
|
self.1
|
||
|
|
.contains(&idx)
|
||
|
|
.not()
|
||
|
|
.then(|| match c {
|
||
|
|
NightChange::Protection {
|
||
|
|
target: t,
|
||
|
|
protection,
|
||
|
|
} => (t == target).then_some(protection),
|
||
|
|
_ => None,
|
||
|
|
})
|
||
|
|
.flatten()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn shapeshifter(&self) -> Option<&'a CharacterId> {
|
||
|
|
self.0.iter().enumerate().find_map(|(idx, c)| {
|
||
|
|
self.1
|
||
|
|
.contains(&idx)
|
||
|
|
.not()
|
||
|
|
.then_some(match c {
|
||
|
|
NightChange::Shapeshift { source } => Some(source),
|
||
|
|
_ => None,
|
||
|
|
})
|
||
|
|
.flatten()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn wolf_pack_kill_target(&self) -> Option<&'a CharacterId> {
|
||
|
|
self.0.iter().enumerate().find_map(|(idx, c)| {
|
||
|
|
self.1
|
||
|
|
.contains(&idx)
|
||
|
|
.not()
|
||
|
|
.then_some(match c {
|
||
|
|
NightChange::Kill {
|
||
|
|
target,
|
||
|
|
died_to:
|
||
|
|
DiedTo::Wolfpack {
|
||
|
|
night: _,
|
||
|
|
killing_wolf: _,
|
||
|
|
},
|
||
|
|
} => Some(target),
|
||
|
|
_ => None,
|
||
|
|
})
|
||
|
|
.flatten()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mod taken {
|
||
|
|
use core::ops::Deref;
|
||
|
|
|
||
|
|
pub struct Taken<'a, T>(usize, &'a T);
|
||
|
|
impl<'a, T> Taken<'a, T> {
|
||
|
|
pub const fn new(idx: usize, item: &'a T) -> Self {
|
||
|
|
Self(idx, item)
|
||
|
|
}
|
||
|
|
|
||
|
|
pub const fn idx(&self) -> usize {
|
||
|
|
self.0
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl<'a, T> Deref for Taken<'a, T> {
|
||
|
|
type Target = T;
|
||
|
|
|
||
|
|
fn deref(&self) -> &Self::Target {
|
||
|
|
&self.1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|