diff options
Diffstat (limited to 'src/bt.rs')
-rw-r--r-- | src/bt.rs | 111 |
1 files changed, 30 insertions, 81 deletions
@@ -1,4 +1,4 @@ -use super::api::{Api, ApiError, Skill, Specialization}; +use super::api::{Api, ApiError, Profession, Skill, Specialization}; use num_enum::{IntoPrimitive, TryFromPrimitive}; use std::{convert::TryFrom, error::Error, fmt, str::FromStr}; @@ -11,7 +11,6 @@ pub enum ChatlinkError { error_froms! { ChatlinkError, err: ApiError => ChatlinkError::ApiError(err), _err: base64::DecodeError => ChatlinkError::MalformedInput, - _err: num_enum::TryFromPrimitiveError<Profession> => ChatlinkError::MalformedInput, _err: num_enum::TryFromPrimitiveError<TraitChoice> => ChatlinkError::MalformedInput, _err: num_enum::TryFromPrimitiveError<Legend> => ChatlinkError::MalformedInput, } @@ -34,48 +33,6 @@ impl Error for ChatlinkError { } } -/// The profession of the template. -/// -/// Can be cast to an `u8` to get the right ID for building chat links. -#[repr(u8)] -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, IntoPrimitive, TryFromPrimitive)] -pub enum Profession { - Guardian = 1, - Warrior = 2, - Engineer = 3, - Ranger = 4, - Thief = 5, - Elementalist = 6, - Mesmer = 7, - Necromancer = 8, - Revenant = 9, -} - -impl fmt::Display for Profession { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self, f) - } -} - -impl FromStr for Profession { - type Err = (); - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "Guardian" => Ok(Profession::Guardian), - "Warrior" => Ok(Profession::Warrior), - "Engineer" => Ok(Profession::Engineer), - "Ranger" => Ok(Profession::Ranger), - "Thief" => Ok(Profession::Thief), - "Elementalist" => Ok(Profession::Elementalist), - "Mesmer" => Ok(Profession::Mesmer), - "Necromancer" => Ok(Profession::Necromancer), - "Revenant" => Ok(Profession::Revenant), - _ => Err(()), - } - } -} - /// Represents the selected trait. #[repr(u8)] #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, IntoPrimitive, TryFromPrimitive)] @@ -176,6 +133,12 @@ pub const SKILL_COUNT: usize = 5; pub const TRAITLINE_COUNT: usize = 3; pub const LEGEND_COUNT: usize = 4; +/// The code for the revenant profession. +pub const CODE_REVENANT: u32 = 9; + +pub const EMPTY_SKILLS: [Option<Skill>; SKILL_COUNT] = [None, None, None, None, None]; +pub const EMPTY_TRAITLINES: [Option<Traitline>; TRAITLINE_COUNT] = [None,None, None]; + /// Represents a build template. /// /// This struct is made with the same limitations as the game imposes. That is, even though the @@ -201,8 +164,8 @@ impl BuildTemplate { pub fn empty(profession: Profession) -> BuildTemplate { BuildTemplate { profession, - skills: [None, None, None, None, None], - traitlines: [None, None, None], + skills: EMPTY_SKILLS, + traitlines: EMPTY_TRAITLINES, extra_data: ExtraData::None, } } @@ -239,8 +202,8 @@ impl BuildTemplate { } /// Returns the profession of this build. - pub fn profession(&self) -> Profession { - self.profession + pub fn profession(&self) -> &Profession { + &self.profession } /// Returns the skills of this build. @@ -273,7 +236,7 @@ impl BuildTemplate { /// The returned link is ready to be copy-and-pasted into Guild Wars 2. pub fn chatlink(&self) -> String { let mut bytes = vec![0x0Du8]; - let prof_byte = self.profession() as u8; + let prof_byte = self.profession().code as u8; bytes.push(prof_byte); for traitline in self.traitlines().iter() { @@ -295,7 +258,10 @@ impl BuildTemplate { bytes.push(0); } Some(s) => { - let palette_id = skill_id_to_palette_id(s.id); + let palette_id = self + .profession() + .skill_id_to_palette_id(s.id) + .unwrap_or(0); bytes.push((palette_id & 0xFF) as u8); bytes.push(((palette_id >> 8) & 0xFF) as u8); } @@ -338,9 +304,9 @@ impl BuildTemplate { } bytes.remove(0); - let profession = Profession::try_from(bytes.remove(0))?; + let profession = code_to_profession(api, bytes.remove(0) as u32)?; - let mut traitlines = BuildTemplate::empty(profession).traitlines; + let mut traitlines = EMPTY_TRAITLINES; for i in traitlines.iter_mut() { let spec_id = bytes.remove(0); let trait_choices = bytes.remove(0); @@ -355,14 +321,16 @@ impl BuildTemplate { *i = Some((spec, [c_0, c_1, c_2])); } - let mut skills = BuildTemplate::empty(profession).skills; + let mut skills = EMPTY_SKILLS; for i in skills.iter_mut() { // Terrestrial let byte_1 = bytes.remove(0); let byte_2 = bytes.remove(0); let palette_id = byte_1 as u32 | (byte_2 as u32) << 8; if palette_id != 0 { - let skill_id = palette_id_to_skill_id(palette_id); + let skill_id = profession + .palette_id_to_skill_id(palette_id) + .ok_or(ChatlinkError::MalformedInput)?; let skill = api.get_skills(&[skill_id])?.remove(0); *i = Some(skill); } @@ -372,8 +340,8 @@ impl BuildTemplate { bytes.remove(0); } - let extra_data = match profession { - Profession::Revenant => { + let extra_data = match profession.code { + CODE_REVENANT => { let mut legends = [Legend::None; LEGEND_COUNT]; for i in legends.iter_mut() { *i = Legend::try_from(bytes.remove(0))?; @@ -392,29 +360,10 @@ impl BuildTemplate { } } -lazy_static! { - static ref PALETTE_MAPPING: Vec<(u32, u32)> = - serde_json::from_str(include_str!("skill_palette.json")).unwrap(); -} - -// Those functions do linear searches, but the list only has about 400 items, which should be okay. -// If performance becomes an issue, we can always create hash tables or do a binary search, -// however, since we need both directions, we would need double the memory to keep the second map. - -fn skill_id_to_palette_id(input: u32) -> u32 { - for (skill, palette) in PALETTE_MAPPING.iter() { - if *skill == input { - return *palette; - } - } - 0 -} - -fn palette_id_to_skill_id(input: u32) -> u32 { - for (skill, palette) in PALETTE_MAPPING.iter() { - if *palette == input { - return *skill; - } - } - 0 +fn code_to_profession(api: &mut Api, code: u32) -> Result<Profession, ChatlinkError> { + let professions = api.get_all_professions()?; + professions + .into_iter() + .find(|p| p.code == code) + .ok_or(ChatlinkError::MalformedInput) } |