diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/analyzers/mod.rs | 56 | ||||
| -rw-r--r-- | src/analyzers/raids/mod.rs | 2 | ||||
| -rw-r--r-- | src/analyzers/raids/w6.rs | 10 | ||||
| -rw-r--r-- | src/gamedata.rs | 197 | ||||
| -rw-r--r-- | src/lib.rs | 16 | 
5 files changed, 138 insertions, 143 deletions
| diff --git a/src/analyzers/mod.rs b/src/analyzers/mod.rs index 28724a2..ff04728 100644 --- a/src/analyzers/mod.rs +++ b/src/analyzers/mod.rs @@ -24,7 +24,7 @@  //! [`Log::analyzer`][Log::analyzer] (or [`for_log`][for_log]) and the methods defined in  //! [`Analyzer`][Analyzer]. -use crate::{Boss, Log}; +use crate::{Encounter, Log};  pub mod fractals;  pub mod helpers; @@ -82,41 +82,43 @@ pub fn for_log<'l>(log: &'l Log) -> Option<Box<dyn Analyzer + 'l>> {      let boss = log.encounter()?;      match boss { -        Boss::ValeGuardian | Boss::Gorseval | Boss::Sabetha => { +        Encounter::ValeGuardian | Encounter::Gorseval | Encounter::Sabetha => {              Some(Box::new(raids::GenericRaid::new(log)))          } -        Boss::Slothasor | Boss::Matthias => Some(Box::new(raids::GenericRaid::new(log))), +        Encounter::Slothasor | Encounter::Matthias => Some(Box::new(raids::GenericRaid::new(log))), -        Boss::KeepConstruct => Some(Box::new(raids::GenericRaid::new(log))), -        Boss::Xera => Some(Box::new(raids::Xera::new(log))), +        Encounter::KeepConstruct => Some(Box::new(raids::GenericRaid::new(log))), +        Encounter::Xera => Some(Box::new(raids::Xera::new(log))), -        Boss::Cairn => Some(Box::new(raids::Cairn::new(log))), -        Boss::MursaatOverseer => Some(Box::new(raids::MursaatOverseer::new(log))), -        Boss::Samarog => Some(Box::new(raids::Samarog::new(log))), -        Boss::Deimos => Some(Box::new(raids::Deimos::new(log))), +        Encounter::Cairn => Some(Box::new(raids::Cairn::new(log))), +        Encounter::MursaatOverseer => Some(Box::new(raids::MursaatOverseer::new(log))), +        Encounter::Samarog => Some(Box::new(raids::Samarog::new(log))), +        Encounter::Deimos => Some(Box::new(raids::Deimos::new(log))), -        Boss::SoullessHorror => Some(Box::new(raids::SoullessHorror::new(log))), -        Boss::Dhuum => Some(Box::new(raids::Dhuum::new(log))), +        Encounter::SoullessHorror => Some(Box::new(raids::SoullessHorror::new(log))), +        Encounter::VoiceInTheVoid => Some(Box::new(raids::Dhuum::new(log))), -        Boss::ConjuredAmalgamate => Some(Box::new(raids::ConjuredAmalgamate::new(log))), -        Boss::LargosTwins => Some(Box::new(raids::LargosTwins::new(log))), -        Boss::Qadim => Some(Box::new(raids::Qadim::new(log))), +        Encounter::ConjuredAmalgamate => Some(Box::new(raids::ConjuredAmalgamate::new(log))), +        Encounter::TwinLargos => Some(Box::new(raids::TwinLargos::new(log))), +        Encounter::Qadim => Some(Box::new(raids::Qadim::new(log))), -        Boss::CardinalAdina => Some(Box::new(raids::CardinalAdina::new(log))), -        Boss::CardinalSabir => Some(Box::new(raids::CardinalSabir::new(log))), -        Boss::QadimThePeerless => Some(Box::new(raids::QadimThePeerless::new(log))), +        Encounter::CardinalAdina => Some(Box::new(raids::CardinalAdina::new(log))), +        Encounter::CardinalSabir => Some(Box::new(raids::CardinalSabir::new(log))), +        Encounter::QadimThePeerless => Some(Box::new(raids::QadimThePeerless::new(log))), -        Boss::Ai => Some(Box::new(fractals::Ai::new(log))), -        Boss::Skorvald => Some(Box::new(fractals::Skorvald::new(log))), -        Boss::Artsariiv | Boss::Arkk | Boss::MAMA | Boss::Siax | Boss::Ensolyss => { -            Some(Box::new(fractals::GenericFractal::new(log))) -        } +        Encounter::Ai => Some(Box::new(fractals::Ai::new(log))), +        Encounter::Skorvald => Some(Box::new(fractals::Skorvald::new(log))), +        Encounter::Artsariiv +        | Encounter::Arkk +        | Encounter::MAMA +        | Encounter::Siax +        | Encounter::Ensolyss => Some(Box::new(fractals::GenericFractal::new(log))), -        Boss::IcebroodConstruct -        | Boss::VoiceOfTheFallen -        | Boss::FraenirOfJormag -        | Boss::Boneskinner -        | Boss::WhisperOfJormag => Some(Box::new(strikes::GenericStrike::new(log))), +        Encounter::IcebroodConstruct +        | Encounter::SuperKodanBrothers +        | Encounter::FraenirOfJormag +        | Encounter::Boneskinner +        | Encounter::WhisperOfJormag => Some(Box::new(strikes::GenericStrike::new(log))),      }  } diff --git a/src/analyzers/raids/mod.rs b/src/analyzers/raids/mod.rs index bb3824b..a0602b0 100644 --- a/src/analyzers/raids/mod.rs +++ b/src/analyzers/raids/mod.rs @@ -19,7 +19,7 @@ mod w5;  pub use w5::{Dhuum, SoullessHorror};  mod w6; -pub use w6::{ConjuredAmalgamate, LargosTwins, Qadim}; +pub use w6::{ConjuredAmalgamate, TwinLargos, Qadim};  mod w7;  pub use w7::{CardinalAdina, CardinalSabir, QadimThePeerless}; diff --git a/src/analyzers/raids/w6.rs b/src/analyzers/raids/w6.rs index 8701a63..11fd02e 100644 --- a/src/analyzers/raids/w6.rs +++ b/src/analyzers/raids/w6.rs @@ -59,21 +59,21 @@ pub const LARGOS_CM_HEALTH: u64 = 19_200_000;  ///  /// The CM is detected by the boss's health, which is higher in the challenge mote.  #[derive(Debug, Clone, Copy)] -pub struct LargosTwins<'log> { +pub struct TwinLargos<'log> {      log: &'log Log,  } -impl<'log> LargosTwins<'log> { -    /// Create a new [`LargosTwins`] analyzer for the given log. +impl<'log> TwinLargos<'log> { +    /// Create a new [`TwinLargos`] analyzer for the given log.      ///      /// **Do not** use this method unless you know what you are doing. Instead, rely on      /// [`Log::analyzer`]!      pub fn new(log: &'log Log) -> Self { -        LargosTwins { log } +        TwinLargos { log }      }  } -impl<'log> Analyzer for LargosTwins<'log> { +impl<'log> Analyzer for TwinLargos<'log> {      fn log(&self) -> &Log {          self.log      } diff --git a/src/gamedata.rs b/src/gamedata.rs index 392bd01..d307802 100644 --- a/src/gamedata.rs +++ b/src/gamedata.rs @@ -6,9 +6,17 @@ use std::{  };  use thiserror::Error; -/// Enum containing all bosses with their IDs. +/// Enum containing all encounters with their IDs. +/// +/// An encounter is a fight or event, consisting of no, one or multiple bosses. Most encounters map +/// 1:1 to a boss (like Vale Guardian), however there are some encounters with multiple bosses +/// (like Twin Largos), and even encounters without bosses (like the River of Souls). +/// +/// This enum is non-exhaustive to ensure that future added encounters can be added without +/// inducing a breaking change.  #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive)] -pub enum Boss { +#[non_exhaustive] +pub enum Encounter {      // Wing 1      ValeGuardian = 0x3C4E,      Gorseval = 0x3C45, @@ -20,11 +28,6 @@ pub enum Boss {      // Wing 3      KeepConstruct = 0x3F6B, -    /// Xera ID for phase 1. -    /// -    /// This is only half of Xera's ID, as there will be a second agent for the -    /// second phase. This agent will have another ID, see -    /// [`XERA_PHASE2_ID`](constant.XERA_PHASE2_ID.html).      Xera = 0x3F76,      // Wing 4 @@ -35,15 +38,11 @@ pub enum Boss {      // Wing 5      SoullessHorror = 0x4D37, -    Dhuum = 0x4BFA, +    VoiceInTheVoid = 0x4BFA,      // Wing 6      ConjuredAmalgamate = 0xABC6, -    /// This is the ID of Nikare, as that is what the Twin Largos logs are identified by. -    /// -    /// If you want Nikare specifically, consider using [`NIKARE_ID`][NIKARE_ID], and similarly, if -    /// you need Kenut, you can use [`KENUT_ID`][KENUT_ID]. -    LargosTwins = 0x5271, +    TwinLargos = 0x5271,      Qadim = 0x51C6,      // Wing 7 @@ -66,110 +65,104 @@ pub enum Boss {      // Strike missions      IcebroodConstruct = 0x568A, -    /// This is the ID of the Voice of the Fallen. -    /// -    /// The strike mission itself contains two bosses, the Voice of the Fallen and the Claw of the -    /// Fallen. Consider using either [`VOICE_OF_THE_FALLEN_ID`][VOICE_OF_THE_FALLEN_ID] or -    /// [`CLAW_OF_THE_FALLEN_ID`][CLAW_OF_THE_FALLEN_ID] if you refer to one of those bosses -    /// specifically. -    VoiceOfTheFallen = 0x5747, +    SuperKodanBrothers = 0x5747,      FraenirOfJormag = 0x57DC,      Boneskinner = 0x57F9,      WhisperOfJormag = 0x58B7,  } -/// Error for when converting a string to the boss fails. +/// Error for when converting a string to an encounter fails.  #[derive(Debug, Clone, Hash, PartialEq, Eq, Error)] -#[error("Invalid boss identifier: {0}")] -pub struct ParseBossError(String); +#[error("Invalid encounter identifier: {0}")] +pub struct ParseEncounterError(String); -impl FromStr for Boss { -    type Err = ParseBossError; +impl FromStr for Encounter { +    type Err = ParseEncounterError;      fn from_str(s: &str) -> Result<Self, Self::Err> {          let lower = s.to_lowercase();          match &lower as &str { -            "vg" | "vale guardian" => Ok(Boss::ValeGuardian), -            "gorse" | "gorseval" => Ok(Boss::Gorseval), -            "sab" | "sabetha" => Ok(Boss::Sabetha), +            "vg" | "vale guardian" => Ok(Encounter::ValeGuardian), +            "gorse" | "gorseval" => Ok(Encounter::Gorseval), +            "sab" | "sabetha" => Ok(Encounter::Sabetha), -            "sloth" | "slothasor" => Ok(Boss::Slothasor), -            "matthias" => Ok(Boss::Matthias), +            "sloth" | "slothasor" => Ok(Encounter::Slothasor), +            "matthias" => Ok(Encounter::Matthias), -            "kc" | "keep construct" => Ok(Boss::KeepConstruct), -            "xera" => Ok(Boss::Xera), +            "kc" | "keep construct" => Ok(Encounter::KeepConstruct), +            "xera" => Ok(Encounter::Xera), -            "cairn" => Ok(Boss::Cairn), -            "mo" | "mursaat overseer" => Ok(Boss::MursaatOverseer), -            "sam" | "sama" | "samarog" => Ok(Boss::Samarog), -            "deimos" => Ok(Boss::Deimos), +            "cairn" => Ok(Encounter::Cairn), +            "mo" | "mursaat overseer" => Ok(Encounter::MursaatOverseer), +            "sam" | "sama" | "samarog" => Ok(Encounter::Samarog), +            "deimos" => Ok(Encounter::Deimos), -            "desmina" | "sh" | "soulless horror" => Ok(Boss::SoullessHorror), -            "dhuum" => Ok(Boss::Dhuum), +            "desmina" | "sh" | "soulless horror" => Ok(Encounter::SoullessHorror), +            "dhuum" | "voice in the void" => Ok(Encounter::VoiceInTheVoid), -            "ca" | "conjured amalgamate" => Ok(Boss::ConjuredAmalgamate), -            "largos" | "twins" | "largos twins" => Ok(Boss::LargosTwins), -            "qadim" => Ok(Boss::Qadim), +            "ca" | "conjured amalgamate" => Ok(Encounter::ConjuredAmalgamate), +            "largos" | "twins" | "largos twins" => Ok(Encounter::TwinLargos), +            "qadim" => Ok(Encounter::Qadim), -            "adina" | "cardinal adina" => Ok(Boss::CardinalAdina), -            "sabir" | "cardinal sabir" => Ok(Boss::CardinalSabir), -            "qadimp" | "peerless qadim" | "qadim the peerless" => Ok(Boss::QadimThePeerless), +            "adina" | "cardinal adina" => Ok(Encounter::CardinalAdina), +            "sabir" | "cardinal sabir" => Ok(Encounter::CardinalSabir), +            "qadimp" | "peerless qadim" | "qadim the peerless" => Ok(Encounter::QadimThePeerless), -            "ai" | "ai keeper of the peak" => Ok(Boss::Ai), +            "ai" | "ai keeper of the peak" => Ok(Encounter::Ai), -            "skorvald" => Ok(Boss::Skorvald), -            "artsariiv" => Ok(Boss::Artsariiv), -            "arkk" => Ok(Boss::Arkk), +            "skorvald" => Ok(Encounter::Skorvald), +            "artsariiv" => Ok(Encounter::Artsariiv), +            "arkk" => Ok(Encounter::Arkk), -            "mama" => Ok(Boss::MAMA), -            "siax" => Ok(Boss::Siax), -            "ensolyss" | "ensolyss of the endless torment" => Ok(Boss::Ensolyss), +            "mama" => Ok(Encounter::MAMA), +            "siax" => Ok(Encounter::Siax), +            "ensolyss" | "ensolyss of the endless torment" => Ok(Encounter::Ensolyss), -            "icebrood" | "icebrood construct" => Ok(Boss::IcebroodConstruct), -            "kodans" | "super kodan brothers" => Ok(Boss::VoiceOfTheFallen), -            "fraenir" | "fraenir of jormag" => Ok(Boss::FraenirOfJormag), -            "boneskinner" => Ok(Boss::Boneskinner), -            "whisper" | "whisper of jormag" => Ok(Boss::WhisperOfJormag), +            "icebrood" | "icebrood construct" => Ok(Encounter::IcebroodConstruct), +            "kodans" | "super kodan brothers" => Ok(Encounter::SuperKodanBrothers), +            "fraenir" | "fraenir of jormag" => Ok(Encounter::FraenirOfJormag), +            "boneskinner" => Ok(Encounter::Boneskinner), +            "whisper" | "whisper of jormag" => Ok(Encounter::WhisperOfJormag), -            _ => Err(ParseBossError(s.to_owned())), +            _ => Err(ParseEncounterError(s.to_owned())),          }      }  } -impl Display for Boss { +impl Display for Encounter {      fn fmt(&self, f: &mut Formatter) -> fmt::Result {          let name = match *self { -            Boss::ValeGuardian => "Vale Guardian", -            Boss::Gorseval => "Gorseval", -            Boss::Sabetha => "Sabetha", -            Boss::Slothasor => "Slothasor", -            Boss::Matthias => "Matthias Gabrel", -            Boss::KeepConstruct => "Keep Construct", -            Boss::Xera => "Xera", -            Boss::Cairn => "Cairn the Indomitable", -            Boss::MursaatOverseer => "Mursaat Overseer", -            Boss::Samarog => "Samarog", -            Boss::Deimos => "Deimos", -            Boss::SoullessHorror => "Soulless Horror", -            Boss::Dhuum => "Dhuum", -            Boss::ConjuredAmalgamate => "Conjured Amalgamate", -            Boss::LargosTwins => "Twin Largos", -            Boss::Qadim => "Qadim", -            Boss::CardinalAdina => "Cardinal Adina", -            Boss::CardinalSabir => "Cardinal Sabir", -            Boss::QadimThePeerless => "Qadim the Peerless", -            Boss::Ai => "Ai Keeper of the Peak", -            Boss::Skorvald => "Skorvald the Shattered", -            Boss::Artsariiv => "Artsariiv", -            Boss::Arkk => "Arkk", -            Boss::MAMA => "MAMA", -            Boss::Siax => "Siax the Corrupted", -            Boss::Ensolyss => "Ensolyss of the Endless Torment", -            Boss::IcebroodConstruct => "Icebrood Construct", -            Boss::VoiceOfTheFallen => "Super Kodan Brothers", -            Boss::FraenirOfJormag => "Fraenir of Jormag", -            Boss::Boneskinner => "Boneskinner", -            Boss::WhisperOfJormag => "Whisper of Jormag", +            Encounter::ValeGuardian => "Vale Guardian", +            Encounter::Gorseval => "Gorseval", +            Encounter::Sabetha => "Sabetha", +            Encounter::Slothasor => "Slothasor", +            Encounter::Matthias => "Matthias Gabrel", +            Encounter::KeepConstruct => "Keep Construct", +            Encounter::Xera => "Xera", +            Encounter::Cairn => "Cairn the Indomitable", +            Encounter::MursaatOverseer => "Mursaat Overseer", +            Encounter::Samarog => "Samarog", +            Encounter::Deimos => "Deimos", +            Encounter::SoullessHorror => "Soulless Horror", +            Encounter::VoiceInTheVoid => "Voice in the Void", +            Encounter::ConjuredAmalgamate => "Conjured Amalgamate", +            Encounter::TwinLargos => "Twin Largos", +            Encounter::Qadim => "Qadim", +            Encounter::CardinalAdina => "Cardinal Adina", +            Encounter::CardinalSabir => "Cardinal Sabir", +            Encounter::QadimThePeerless => "Qadim the Peerless", +            Encounter::Ai => "Ai Keeper of the Peak", +            Encounter::Skorvald => "Skorvald the Shattered", +            Encounter::Artsariiv => "Artsariiv", +            Encounter::Arkk => "Arkk", +            Encounter::MAMA => "MAMA", +            Encounter::Siax => "Siax the Corrupted", +            Encounter::Ensolyss => "Ensolyss of the Endless Torment", +            Encounter::IcebroodConstruct => "Icebrood Construct", +            Encounter::SuperKodanBrothers => "Super Kodan Brothers", +            Encounter::FraenirOfJormag => "Fraenir of Jormag", +            Encounter::Boneskinner => "Boneskinner", +            Encounter::WhisperOfJormag => "Whisper of Jormag",          };          write!(f, "{}", name)      } @@ -184,12 +177,12 @@ impl Display for Boss {  pub const XERA_PHASE2_ID: u16 = 0x3F9E;  /// The ID of Nikare in the Twin Largos fight. -pub const NIKARE_ID: u16 = Boss::LargosTwins as u16; +pub const NIKARE_ID: u16 = Encounter::TwinLargos as u16;  /// The ID of Kenut in the Twin Largos fight.  pub const KENUT_ID: u16 = 21089;  /// The ID of the Voice of the Fallen. -pub const VOICE_OF_THE_FALLEN_ID: u16 = Boss::VoiceOfTheFallen as u16; +pub const VOICE_OF_THE_FALLEN_ID: u16 = Encounter::SuperKodanBrothers as u16;  /// The ID of the Claw of the Fallen.  pub const CLAW_OF_THE_FALLEN_ID: u16 = 22481; @@ -369,9 +362,9 @@ mod tests {      use super::*;      #[test] -    fn test_boss_parsing_ok() { -        use Boss::*; -        let tests: &[(&'static str, Boss)] = &[ +    fn test_encounter_parsing_ok() { +        use Encounter::*; +        let tests: &[(&'static str, Encounter)] = &[              ("vg", ValeGuardian),              ("VG", ValeGuardian),              ("vale guardian", ValeGuardian), @@ -408,14 +401,14 @@ mod tests {              ("soulless horror", SoullessHorror),              ("desmina", SoullessHorror),              ("Desmina", SoullessHorror), -            ("dhuum", Dhuum), -            ("Dhuum", Dhuum), +            ("dhuum", VoiceInTheVoid), +            ("Dhuum", VoiceInTheVoid),              ("ca", ConjuredAmalgamate),              ("conjured amalgamate", ConjuredAmalgamate),              ("Conjured Amalgamate", ConjuredAmalgamate), -            ("largos", LargosTwins), -            ("twins", LargosTwins), -            ("largos twins", LargosTwins), +            ("largos", TwinLargos), +            ("twins", TwinLargos), +            ("largos twins", TwinLargos),              ("qadim", Qadim),              ("Qadim", Qadim),              ("adina", CardinalAdina), @@ -445,7 +438,7 @@ mod tests {              ("fraenir", FraenirOfJormag),              ("Fraenir of Jormag", FraenirOfJormag),              ("boneskinner", Boneskinner), -            ("kodans", VoiceOfTheFallen), +            ("kodans", SuperKodanBrothers),              ("whisper", WhisperOfJormag),              ("Whisper of Jormag", WhisperOfJormag),          ]; @@ -461,7 +454,7 @@ mod tests {      }      #[test] -    fn test_boss_parsing_err() { +    fn test_encounter_parsing_err() {          let tests = &[              "",              "vga", @@ -475,7 +468,7 @@ mod tests {              "cardinal",          ];          for test in tests { -            assert!(test.parse::<Boss>().is_err()); +            assert!(test.parse::<Encounter>().is_err());          }      } @@ -104,7 +104,7 @@ mod processing;  pub use processing::{process, process_file, process_stream, Compression};  pub mod gamedata; -pub use gamedata::{Boss, EliteSpec, Profession}; +pub use gamedata::{EliteSpec, Encounter, Profession};  pub mod analyzers;  pub use analyzers::{Analyzer, Outcome}; @@ -758,11 +758,11 @@ impl Log {      /// This correctly returns multiple agents on encounters where multiple      /// agents are needed.      pub fn boss_agents(&self) -> Vec<&Agent> { -        let boss_ids = if self.boss_id == Boss::Xera as u16 { +        let boss_ids = if self.boss_id == Encounter::Xera as u16 {              vec![self.boss_id, gamedata::XERA_PHASE2_ID] -        } else if self.boss_id == Boss::LargosTwins as u16 { +        } else if self.boss_id == Encounter::TwinLargos as u16 {              vec![gamedata::NIKARE_ID, gamedata::KENUT_ID] -        } else if self.encounter() == Some(Boss::VoiceOfTheFallen) { +        } else if self.encounter() == Some(Encounter::SuperKodanBrothers) {              vec![                  gamedata::VOICE_OF_THE_FALLEN_ID,                  gamedata::CLAW_OF_THE_FALLEN_ID, @@ -791,16 +791,16 @@ impl Log {      ///      /// Some logs don't have an encounter set or have an ID that is unknown to us (for example, if      /// people set up arcdps with custom IDs). Therefore, this method can only return the encounter -    /// if we know about it in [`Boss`][Boss]. +    /// if we know about it in [`Encounter`].      #[inline] -    pub fn encounter(&self) -> Option<Boss> { +    pub fn encounter(&self) -> Option<Encounter> {          // Sometimes, encounters of the strike mission "Voice of the Fallen and Claw of the Fallen"          // are saved with the ID of the Claw and sometimes with the Voice. Therefore, we need to          // unify those cases.          if self.boss_id == gamedata::CLAW_OF_THE_FALLEN_ID { -            return Some(Boss::VoiceOfTheFallen); +            return Some(Encounter::SuperKodanBrothers);          } -        Boss::from_u16(self.boss_id) +        Encounter::from_u16(self.boss_id)      }      /// Return an analyzer suitable to analyze the given log. | 
