use crate::ally::Ally;
use crate::allyset::AllySet;
#[derive(Debug)]
pub(crate) enum DeathReason {
NoArmorUpgrade,
NoShieldUpgrade,
NoWeaponUpgrade,
BadBiotic,
}
impl DeathReason {
pub(crate) fn get_victim(&self, allies: AllySet) -> Ally {
match self
.victim_priority()
.iter()
.copied()
.find(|&ally| allies.contains(ally))
{
Some(victim) => victim,
None => panic!("no victim in {allies:?} for {self:?}"),
}
}
fn victim_priority(&self) -> &'static [Ally] {
match self {
Self::NoArmorUpgrade => &[Ally::Jack],
Self::NoShieldUpgrade => &[
Ally::Kasumi,
Ally::Legion,
Ally::Tali,
Ally::Thane,
Ally::Garrus,
Ally::Zaeed,
Ally::Grunt,
Ally::Samara,
Ally::Morinth,
],
Self::NoWeaponUpgrade => &[
Ally::Thane,
Ally::Garrus,
Ally::Zaeed,
Ally::Grunt,
Ally::Jack,
Ally::Samara,
Ally::Morinth,
],
Self::BadBiotic => &[
Ally::Thane,
Ally::Jack,
Ally::Garrus,
Ally::Legion,
Ally::Grunt,
Ally::Samara,
Ally::Jacob,
Ally::Mordin,
Ally::Tali,
Ally::Kasumi,
Ally::Zaeed,
Ally::Morinth,
],
}
}
}
pub(crate) fn get_defense_victims(defenders: AllySet, loyal: AllySet) -> AllySet {
let toll = defense_toll(defenders, loyal);
let disloyal = defenders - loyal;
let disloyal_victims = DEFENSE_VICTIM_PRIORITY
.iter()
.copied()
.filter(|&ally| disloyal.contains(ally));
let loyal = defenders & loyal;
let loyal_victims = DEFENSE_VICTIM_PRIORITY
.iter()
.copied()
.filter(|&ally| loyal.contains(ally));
disloyal_victims.chain(loyal_victims).take(toll).collect()
}
const DEFENSE_VICTIM_PRIORITY: &[Ally] = &[
Ally::Mordin,
Ally::Tali,
Ally::Kasumi,
Ally::Jack,
Ally::Miranda,
Ally::Jacob,
Ally::Garrus,
Ally::Samara,
Ally::Morinth,
Ally::Legion,
Ally::Thane,
Ally::Zaeed,
Ally::Grunt,
];
fn defense_score(ally: Ally, loyal: AllySet) -> u8 {
use Ally::*;
let base_score = match ally {
Garrus | Grunt | Zaeed => 4,
Jacob | Legion | Miranda | Morinth | Samara | Thane => 2,
Jack | Kasumi | Mordin | Tali => 1,
};
let penalty: u8 = (!loyal.contains(ally)).into();
base_score - penalty
}
fn defense_toll(defenders: AllySet, loyal: AllySet) -> usize {
if defenders.is_empty() {
panic!("zero defenders");
}
let defender_count = defenders.len();
let score = defenders
.into_iter()
.map(|ally| defense_score(ally, loyal))
.sum::<u8>() as f32
/ defender_count as f32;
match defender_count {
0 => unreachable!(),
1 => (score < 2.).into(),
2 => {
if score == 0. {
2
} else {
(score < 2.).into()
}
}
3 => {
if score == 0. {
3
} else if score < 1. {
2
} else {
(score < 2.).into()
}
}
4 => {
if score == 0. {
4
} else if score < 0.5 {
3
} else if score <= 1. {
2
} else {
(score < 2.).into()
}
}
_ => {
if score < 0.5 {
3
} else if score < 1.5 {
2
} else {
(score < 2.).into()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use Ally::*;
#[test]
fn get_victim() {
assert_eq!(
Jack,
DeathReason::NoArmorUpgrade.get_victim(AllySet::REQUIRED)
);
let mut allies = Kasumi | Tali | Zaeed;
assert_eq!(Kasumi, DeathReason::NoShieldUpgrade.get_victim(allies));
allies -= Kasumi;
assert_eq!(Tali, DeathReason::NoShieldUpgrade.get_victim(allies));
allies -= Tali;
assert_eq!(Zaeed, DeathReason::NoShieldUpgrade.get_victim(allies));
}
#[test]
#[should_panic = "no victim in AllySet(0x0214) for NoWeaponUpgrade"]
fn get_victim_panic() {
let _victim = DeathReason::NoWeaponUpgrade.get_victim(Tali | Mordin | Miranda);
}
#[test]
fn defense_victims() {
let defenders = Garrus | Jack | Morinth | Thane;
let loyal = defenders - (Garrus | Thane);
let victims = get_defense_victims(defenders, loyal);
assert_eq!(AllySet::from(Garrus), victims);
let defenders = Jacob | Grunt | Mordin | Legion | Kasumi;
let victims = get_defense_victims(defenders, defenders);
assert_eq!(AllySet::NOBODY, victims);
}
}