diff --git a/NetStone/Model/Parseables/Character/ClassJob/CharacterClassJob.cs b/NetStone/Model/Parseables/Character/ClassJob/CharacterClassJob.cs index 2850532..8301698 100644 --- a/NetStone/Model/Parseables/Character/ClassJob/CharacterClassJob.cs +++ b/NetStone/Model/Parseables/Character/ClassJob/CharacterClassJob.cs @@ -21,6 +21,16 @@ public CharacterClassJob(HtmlNode rootNode, CharacterClassJobDefinition definiti this.definition = definition; } + /// + /// Information about the Eureka class. + /// + public ClassJobEureka? Eureka => new ClassJobEureka(this.RootNode, this.definition.Eureka).GetOptional(); + + /// + /// Information about the Bozja class. + /// + public ClassJobBozja? Bozja => new ClassJobBozja(this.RootNode, this.definition.Bozja).GetOptional(); + /// /// Information about the Paladin class. /// @@ -65,7 +75,7 @@ public CharacterClassJob(HtmlNode rootNode, CharacterClassJobDefinition definiti /// Information about the Reaper class. /// public ClassJobEntry Reaper => new(this.RootNode, this.definition.Reaper); - + /// /// Information about the Viper class. /// @@ -214,7 +224,7 @@ public CharacterClassJob(HtmlNode rootNode, CharacterClassJobDefinition definiti { StaticData.ClassJob.Samurai, this.Samurai }, { StaticData.ClassJob.Reaper, this.Reaper }, - + { StaticData.ClassJob.Viper, this.Viper }, { StaticData.ClassJob.Conjurer, this.WhiteMage }, @@ -239,7 +249,7 @@ public CharacterClassJob(HtmlNode rootNode, CharacterClassJobDefinition definiti { StaticData.ClassJob.BlackMage, this.BlackMage }, { StaticData.ClassJob.RedMage, this.RedMage }, - + { StaticData.ClassJob.Pictomancer, this.Pictomancer }, { StaticData.ClassJob.BlueMage, this.BlueMage }, diff --git a/NetStone/Model/Parseables/Character/ClassJob/ClassJobBozja.cs b/NetStone/Model/Parseables/Character/ClassJob/ClassJobBozja.cs new file mode 100644 index 0000000..a7d613b --- /dev/null +++ b/NetStone/Model/Parseables/Character/ClassJob/ClassJobBozja.cs @@ -0,0 +1,125 @@ +using System.Linq; +using System.Text.RegularExpressions; +using HtmlAgilityPack; +using NetStone.Definitions.Model.Character; + +namespace NetStone.Model.Parseables.Character.ClassJob; + +/// +/// Entry for Bozja Field Operation of a character +/// +public class ClassJobBozja : LodestoneParseable, IOptionalParseable +{ + private readonly ClassJobBozjaDefinition definition; + + /// + /// Constructs a new class entry + /// + /// Root node of this entry + /// Parser definition + public ClassJobBozja(HtmlNode rootNode, ClassJobBozjaDefinition definition) : base(rootNode) + { + this.definition = definition; + } + + /// + /// The name of this class and job combo. + /// + public string Name => ParseTooltip(this.definition.NAME); + + /// + /// Value indicating whether this class has its job unlocked. + /// + public bool IsJobUnlocked => this.Name.Contains("/"); + + /// + /// The level this class or job is at. + /// + public int Level + { + get + { + var level = Parse(this.definition.LEVEL); + return level == "-" ? 0 : int.Parse(level); + } + } + + private string ExpString => ParseInnerText(this.definition.METTLE); + + private long? expCurrentVal; + + /// + /// The amount of current achieved EXP on this level. + /// + public long ExpCurrent + { + get + { + if (!this.expCurrentVal.HasValue) + ParseExp(); + + return this.expCurrentVal!.Value; + } + } + + private long? expMaxVal; + + /// + /// The amount of EXP to be reached to gain the next level. + /// + public long ExpMax + { + get + { + if (!this.expCurrentVal.HasValue) + ParseExp(); + + return this.expMaxVal!.Value; + } + } + + /// + /// The outstanding amount of EXP to go to the next level. + /// + public long ExpToGo => this.ExpMax - this.ExpCurrent; + + private void ParseExp() + { + if (!this.Exists) + { + this.expCurrentVal = 0; + this.expMaxVal = 0; + + return; + } + + var expVals = this.ExpString.Split(" / ").Select(x => x.Replace(",", string.Empty)).ToArray(); + + if (expVals[0] == "--") + { + this.expCurrentVal = 0; + this.expMaxVal = 0; + + return; + } + + this.expCurrentVal = long.Parse(Regex.Match(expVals[0], @"\d+").Value); + this.expMaxVal = long.Parse(Regex.Match(expVals[1], @"\d+").Value); + } + + /// + /// Value indicating if this class is unlocked. + /// + public bool Exists => this.Level != 0; + + /// + /// Value indicating if this class is unlocked. + /// + public bool IsUnlocked => this.Exists; + + /// + /// The string representation of this object. + /// + /// Name (Level) + public override string ToString() => $"{this.Name} ({this.Level})"; +} \ No newline at end of file diff --git a/NetStone/Model/Parseables/Character/ClassJob/ClassJobEureka.cs b/NetStone/Model/Parseables/Character/ClassJob/ClassJobEureka.cs new file mode 100644 index 0000000..233d866 --- /dev/null +++ b/NetStone/Model/Parseables/Character/ClassJob/ClassJobEureka.cs @@ -0,0 +1,124 @@ +using System.Linq; +using HtmlAgilityPack; +using NetStone.Definitions.Model.Character; + +namespace NetStone.Model.Parseables.Character.ClassJob; + +/// +/// Entry for Eureka Field Operation of a character +/// +public class ClassJobEureka : LodestoneParseable, IOptionalParseable +{ + private readonly ClassJobEurekaDefinition definition; + + /// + /// Constructs a new class entry + /// + /// Root node of this entry + /// Parser definition + public ClassJobEureka(HtmlNode rootNode, ClassJobEurekaDefinition definition) : base(rootNode) + { + this.definition = definition; + } + + /// + /// The name of this class and job combo. + /// + public string Name => ParseTooltip(this.definition.Name); + + /// + /// Value indicating whether this class has its job unlocked. + /// + public bool IsJobUnlocked => this.Name.Contains("/"); + + /// + /// The level this class or job is at. + /// + public int Level + { + get + { + var level = Parse(this.definition.Level); + return level == "-" ? 0 : int.Parse(level); + } + } + + private string ExpString => ParseInnerText(this.definition.Exp); + + private long? expCurrentVal; + + /// + /// The amount of current achieved EXP on this level. + /// + public long ExpCurrent + { + get + { + if (!this.expCurrentVal.HasValue) + ParseExp(); + + return this.expCurrentVal!.Value; + } + } + + private long? expMaxVal; + + /// + /// The amount of EXP to be reached to gain the next level. + /// + public long ExpMax + { + get + { + if (!this.expCurrentVal.HasValue) + ParseExp(); + + return this.expMaxVal!.Value; + } + } + + /// + /// The outstanding amount of EXP to go to the next level. + /// + public long ExpToGo => this.ExpMax - this.ExpCurrent; + + private void ParseExp() + { + if (!this.Exists) + { + this.expCurrentVal = 0; + this.expMaxVal = 0; + + return; + } + + var expVals = this.ExpString.Split(" / ").Select(x => x.Replace(",", string.Empty)).ToArray(); + + if (expVals[0] == "--") + { + this.expCurrentVal = 0; + this.expMaxVal = 0; + + return; + } + + this.expCurrentVal = long.Parse(expVals[0]); + this.expMaxVal = long.Parse(expVals[1]); + } + + /// + /// Value indicating if this class is unlocked. + /// + public bool Exists => this.Level != 0; + + /// + /// Value indicating if this class is unlocked. + /// + public bool IsUnlocked => this.Exists; + + /// + /// The string representation of this object. + /// + /// Name (Level) + public override string ToString() => $"{this.Name} ({this.Level})"; +} \ No newline at end of file