use core::{num::NonZeroU8, ops::Not}; use super::Result; use crate::{ character::CharacterId, diedto::DiedTo, error::GameError, game::{Village, night::changes::ChangesLookup}, player::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) => { village.character_by_id_mut(character_id)?.kill(died_to); Ok(()) } 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)?; village .character_by_id_mut(original_killer)? .kill(DiedTo::GuardianProtecting { night, source: guardian, protecting: original_target, protecting_from: original_killer, protecting_from_cause: Box::new(original_kill.clone()), }); village.character_by_id_mut(guardian)?.kill(original_kill); Ok(()) } } } } fn resolve_protection( killer: CharacterId, killed_with: &DiedTo, target: CharacterId, protection: &Protection, night: NonZeroU8, ) -> Option { match protection { Protection::Guardian { source, guarding: true, } => Some(KillOutcome::Guarding { original_killer: killer, guardian: *source, original_target: target, original_kill: killed_with.clone(), night, }), Protection::Guardian { source: _, guarding: false, } | Protection::Vindicator { .. } | Protection::Protector { source: _ } => None, } } pub fn resolve_kill( changes: &mut ChangesLookup<'_>, target: CharacterId, died_to: &DiedTo, night: u8, village: &Village, ) -> Result> { 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, died_to, target, &protection, *night).unwrap_or( KillOutcome::Single(*source, 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)?; match changes.protected_take(target) { Some(protection) => { return Ok(resolve_protection( killing_wolf.character_id(), died_to, target, &protection, *night, )); } None => { // Wolf kill went through -- can kill shifter return Ok(Some(KillOutcome::Single( *ss_source, DiedTo::Shapeshift { into: target, night: *night, }, ))); } }; } let protection = match changes.protected_take(target) { Some(prot) => prot, None => return Ok(Some(KillOutcome::Single(target, died_to.clone()))), }; match protection { Protection::Guardian { source, guarding: true, } => Ok(Some(KillOutcome::Guarding { original_killer: died_to .killer() .ok_or(GameError::GuardianInvalidOriginalKill)?, original_target: target, original_kill: died_to.clone(), guardian: source, night: NonZeroU8::new(night).unwrap(), })), Protection::Guardian { guarding: false, .. } | Protection::Vindicator { .. } | Protection::Protector { .. } => Ok(None), } }