diff options
author | Daniel Schadt <kingdread@gmx.de> | 2020-09-23 15:22:55 +0200 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2020-09-23 15:23:55 +0200 |
commit | 39972d54be41bfc7b8b7f38b1f5a4d60e2453da5 (patch) | |
tree | 5ef048c306eca6a67005290859a29699d35978b6 /src | |
parent | 3b79ad8fa9b4a8c9c535b417129e3f70769074e0 (diff) | |
download | evtclib-39972d54be41bfc7b8b7f38b1f5a4d60e2453da5.tar.gz evtclib-39972d54be41bfc7b8b7f38b1f5a4d60e2453da5.tar.bz2 evtclib-39972d54be41bfc7b8b7f38b1f5a4d60e2453da5.zip |
rename Boss to Encounter
This is the first step in differentiating between Encounters and Bosses.
It sounds a bit weird at first, but there are some events without any
bosses (like the River of Souls), and some events which have multiple
bosses (like Twin Largos or the kodan strike mission). If we want to
support this better, without relying on extra IDs, special casing and
constants (like NIKARE_ID), we should differentiate between Encounters
and Bosses.
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. |