diff --git a/Maple2.File.Parser/Maple2.File.Parser.csproj b/Maple2.File.Parser/Maple2.File.Parser.csproj index 45eca3a..ab4ad23 100644 --- a/Maple2.File.Parser/Maple2.File.Parser.csproj +++ b/Maple2.File.Parser/Maple2.File.Parser.csproj @@ -13,7 +13,7 @@ MapleStory2, File, Parser, m2d, xml true - 2.4.32 + 2.4.33 net8.0 README.md enable diff --git a/Maple2.File.Parser/TableParser.cs b/Maple2.File.Parser/TableParser.cs index b058a08..f0329be 100644 --- a/Maple2.File.Parser/TableParser.cs +++ b/Maple2.File.Parser/TableParser.cs @@ -112,6 +112,7 @@ public class TableParser { private readonly XmlSerializer questGroupSerializer; private readonly XmlSerializer darkStreamSerializer; private readonly XmlSerializer clubBuffSerializer; + private readonly XmlSerializer shadowBuffSerializer; private readonly XmlSerializer characterCreateSelectSerializer; private readonly XmlSerializer fameSerializer; private readonly XmlSerializer fameDefSerializer; @@ -232,6 +233,7 @@ public TableParser(M2dReader xmlReader, string language) { questGroupSerializer = new XmlSerializer(typeof(QuestGroupRoot)); darkStreamSerializer = new XmlSerializer(typeof(DarkStreamRoot)); clubBuffSerializer = new XmlSerializer(typeof(ClubBuffRoot)); + shadowBuffSerializer = new XmlSerializer(typeof(ShadowBuffRoot)); characterCreateSelectSerializer = new XmlSerializer(typeof(CharacterCreateSelect)); fameSerializer = new XmlSerializer(typeof(FameRoot)); fameDefSerializer = new XmlSerializer(typeof(FameDef)); @@ -1704,6 +1706,17 @@ public IEnumerable ParseJobTableNew() { } } + public IEnumerable<(int Id, ShadowBuff Buff)> ParseShadowBuff() { + string xml = Sanitizer.RemoveSpaces(xmlReader.GetString(xmlReader.GetEntry("table/shadowbuff.xml"))); + var reader = XmlReader.Create(new StringReader(xml)); + var data = shadowBuffSerializer.Deserialize(reader) as ShadowBuffRoot; + Debug.Assert(data != null); + + foreach (ShadowBuff entry in data.ShadowBuff) { + yield return (entry.id, entry); + } + } + public IEnumerable<(int Id, DungeonRewardCoupon Coupon)> ParseDungeonRewardCoupon() { string xml = Sanitizer.RemoveSpaces(xmlReader.GetString(xmlReader.GetEntry($"table/{locale}/dungeonrewardcoupon.xml"))); var reader = XmlReader.Create(new StringReader(xml)); diff --git a/Maple2.File.Parser/Xml/Table/ShadowBuff.cs b/Maple2.File.Parser/Xml/Table/ShadowBuff.cs new file mode 100644 index 0000000..82cbaba --- /dev/null +++ b/Maple2.File.Parser/Xml/Table/ShadowBuff.cs @@ -0,0 +1,26 @@ +using System.Xml.Serialization; +using M2dXmlGenerator; + +namespace Maple2.File.Parser.Xml.Table; + +// ./data/xml/table/shadowbuff.xml +[XmlRoot("ms2")] +public partial class ShadowBuffRoot { + [M2dFeatureLocale(Selector = "id")] private IList _ShadowBuff; +} + +public partial class ShadowBuff : IFeatureLocale { + [XmlAttribute] public int id; + // Absent on the common traits, which every class lists. + [XmlAttribute] public int jobCode; + [XmlAttribute] public string iconPath = string.Empty; + [XmlElement] public List ShadowBuffLevel; +} + +public partial class ShadowBuffLevel { + [XmlAttribute] public int level; + // Retail's spelling. Additional effect ids, 10 digits, so they do not fit an int. + [M2dArray] public long[] addifectGuids = Array.Empty(); + // Absent on a class's free level 1. + [XmlAttribute] public int levelUpCost; +} diff --git a/Maple2.File.Tests/TableParserTest.cs b/Maple2.File.Tests/TableParserTest.cs index 4dd9b30..5d0049f 100644 --- a/Maple2.File.Tests/TableParserTest.cs +++ b/Maple2.File.Tests/TableParserTest.cs @@ -829,6 +829,27 @@ public void TestClubBuff() { } } + [TestMethod] + public void TestShadowBuff() { + var rows = _parser.ParseShadowBuff().ToList(); + Assert.IsTrue(rows.Count > 0); + foreach ((int id, ShadowBuff buff) in rows) { + Assert.AreEqual(id, buff.id); + Assert.IsTrue(buff.ShadowBuffLevel.Count > 0, $"{id} has no levels"); + foreach (ShadowBuffLevel level in buff.ShadowBuffLevel) { + Assert.IsTrue(level.level is >= 1 and <= 20, $"{id} level {level.level}"); + } + } + + // Soul Binder trait 211001, from table/shadowbuff.xml. + (_, ShadowBuff? soulBinder) = rows.FirstOrDefault(r => r.Id == 211001); + Assert.IsNotNull(soulBinder); + Assert.AreEqual(110, soulBinder.jobCode); + ShadowBuffLevel level2 = soulBinder.ShadowBuffLevel.Single(l => l.level == 2); + Assert.AreEqual(40, level2.levelUpCost); + Assert.AreEqual(9201100102L, level2.addifectGuids[0]); + } + [TestMethod] public void TestDungeonRewardCoupon() { foreach ((_, _) in _parser.ParseDungeonRewardCoupon()) {