use std::fmt::Display;
use serde::{Deserialize, Serialize};
use crate::ally::Ally;
use crate::allyset::AllySet;
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize)]
pub struct SquadSelection {
survivors: AllySet,
victim: Option<Ally>,
}
impl SquadSelection {
pub fn new() -> Self {
Self::default()
}
pub fn add_victim(&mut self, ally: Ally) {
if let Some(victim) = self.victim {
self.survivors |= victim;
}
self.victim = Some(ally);
}
pub fn survivors(&self) -> AllySet {
self.survivors
}
pub fn victim(&self) -> Option<Ally> {
self.victim
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct DecisionPath {
pub recruitment: AllySet,
pub loyalty_missions: AllySet,
pub upgraded_armor: bool,
pub upgraded_shield: bool,
pub cargo_bay_squad: SquadSelection,
pub upgraded_weapon: bool,
pub tech_specialist: Ally,
pub ideal_second_fireteam_leaders: AllySet,
pub second_fireteam_leader_is_ideal: bool,
pub biotic_specialist: Ally,
pub diversion_team_leader: Ally,
pub crew_escort: Option<Ally>,
pub the_long_walk: SquadSelection,
pub final_squad: AllySet,
}
impl Display for DecisionPath {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.upgraded_armor {
writeln!(f, "Purchase the Silaris Armor upgrade.")?;
}
if self.upgraded_shield {
writeln!(f, "Purchase the Cyclonic Shields upgrade.")?;
}
if self.upgraded_weapon {
writeln!(f, "Purchase the Thanix Cannon upgrade.")?;
}
writeln!(f, "Recruit {}.", self.recruitment)?;
let loyalty_mission_allies = match self.loyalty_missions == !Ally::Morinth {
true => AllySet::EVERYONE,
false => self.loyalty_missions,
};
writeln!(f, "Complete loyalty missions for {loyalty_mission_allies}.")?;
if loyalty_mission_allies.contains(Ally::Samara) {
let asari = self.recruitment & AllySet::ASARI;
let possessive = if asari.contains(Ally::Samara) {
"her"
} else {
"Samara's"
};
writeln!(
f,
"- Choose {asari} at the end of {possessive} loyalty mission."
)?;
}
if let Some(victim) = self.cargo_bay_squad.victim() {
let survivors = self.cargo_bay_squad.survivors();
write!(f, "For the cargo bay squad, ")?;
if !survivors.is_empty() {
write!(f, "pick {survivors}")?;
if survivors.len() == 1 {
write!(f, " and ")?;
}
}
if survivors.len() < 2 {
write!(f, "leave {victim} behind")?;
}
writeln!(f, ".")?;
}
writeln!(f, "Choose {} as the tech specialist.", self.tech_specialist)?;
if self.ideal_second_fireteam_leaders.is_empty() {
writeln!(f, "The second fireteam leader does not matter.")?;
} else {
let modifier = (!self.second_fireteam_leader_is_ideal)
.then_some("anyone except ")
.unwrap_or_default();
let ideal_leaders = self
.ideal_second_fireteam_leaders
.to_string_with_conjunction("or");
writeln!(
f,
"Choose {modifier}{ideal_leaders} to lead the second fireteam."
)?;
}
writeln!(
f,
"Choose {} as the biotic specialist.",
self.biotic_specialist
)?;
writeln!(
f,
"Choose {} to lead the diversion team.",
self.diversion_team_leader
)?;
match self.crew_escort {
Some(escort) => writeln!(f, "Send {escort} to escort the crew.")?,
None => writeln!(f, "Do not send anyone to escort the crew.")?,
}
if let Some(victim) = self.the_long_walk.victim() {
let survivors = self.the_long_walk.survivors();
write!(f, "For the squad in the biotic shield, pick {victim}")?;
if !survivors.is_empty() {
write!(f, " and leave {survivors} behind")?;
}
writeln!(f, ".")?;
}
writeln!(f, "For the final squad, pick {}.", self.final_squad)
}
}
#[cfg(test)]
mod tests {
use super::*;
use Ally::*;
#[test]
fn squad() {
let mut squad = SquadSelection::new();
assert_eq!(squad.survivors(), AllySet::NOBODY);
assert_eq!(squad.victim(), None);
squad.add_victim(Jack);
assert_eq!(squad.survivors(), AllySet::NOBODY);
assert_eq!(squad.victim(), Some(Jack));
squad.add_victim(Miranda);
assert_eq!(squad.survivors(), Jack.into());
assert_eq!(squad.victim(), Some(Miranda));
squad.add_victim(Legion);
assert_eq!(squad.survivors(), Jack | Miranda);
assert_eq!(squad.victim(), Some(Legion));
squad.add_victim(Thane);
assert_eq!(squad.survivors(), Jack | Miranda | Legion);
assert_eq!(squad.victim(), Some(Thane));
}
}