From 9f4a4eaa06f3d0136de9088c0e60a0c077248c91 Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Fri, 20 Dec 2019 18:04:24 +0100 Subject: remove hard coded palette IDs Now that the API actually returns the proper palette IDs, we can use those values instead of relying on the hard coded values. This also gets rid of the make_table script that was mostly hackish anyway, and the lazy static HashMap. --- src/bt.rs | 111 +++++++++++++++++--------------------------------------------- 1 file changed, 30 insertions(+), 81 deletions(-) (limited to 'src/bt.rs') diff --git a/src/bt.rs b/src/bt.rs index cf95f3c..8cda030 100644 --- a/src/bt.rs +++ b/src/bt.rs @@ -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 => ChatlinkError::MalformedInput, _err: num_enum::TryFromPrimitiveError => ChatlinkError::MalformedInput, _err: num_enum::TryFromPrimitiveError => 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 { - 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_COUNT] = [None, None, None, None, None]; +pub const EMPTY_TRAITLINES: [Option; 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 { + let professions = api.get_all_professions()?; + professions + .into_iter() + .find(|p| p.code == code) + .ok_or(ChatlinkError::MalformedInput) } -- cgit v1.2.3