diff --git a/OpenKh.Kh2/BaseTable.cs b/OpenKh.Kh2/BaseTable.cs index c0de1a628..7415c72d8 100644 --- a/OpenKh.Kh2/BaseTable.cs +++ b/OpenKh.Kh2/BaseTable.cs @@ -33,7 +33,23 @@ public static void Write(Stream stream, int version, IEnumerable items) } } - + public class BaseList + where T : class + { + public static List Read(Stream stream, int count) + { + return Enumerable.Range(0, count) + .Select(_ => BinaryMapping.ReadObject(stream)) + .ToList(); + } + + public static void Write(Stream stream, IEnumerable items) + { + var itemList = items as IList ?? items.ToList(); + foreach (var item in itemList) + BinaryMapping.WriteObject(stream, item); + } + } public class BaseShortTable where T : class diff --git a/OpenKh.Kh2/Battle/Lvpm.cs b/OpenKh.Kh2/Battle/Lvpm.cs index 2370f30f9..13be8217f 100644 --- a/OpenKh.Kh2/Battle/Lvpm.cs +++ b/OpenKh.Kh2/Battle/Lvpm.cs @@ -1,20 +1,36 @@ using System.Collections.Generic; using System.IO; +using Xe.BinaryMapper; namespace OpenKh.Kh2.Battle { public class Lvpm { - public ushort HpMultiplier { get; set; } // (Hp * HpMultiplier + 99) / 100 - public ushort Strength { get; set; } - public ushort Defense { get; set; } - public ushort MaxStrength { get; set; } - public ushort MinStrength { get; set; } - public ushort Experience { get; set; } + [Data] public ushort HpMultiplier { get; set; } // (Hp * HpMultiplier + 99) / 100 + [Data] public ushort Strength { get; set; } + [Data] public ushort Defense { get; set; } + [Data] public ushort MaxStrength { get; set; } + [Data] public ushort MinStrength { get; set; } + [Data] public ushort Experience { get; set; } - public static List Read(Stream stream) => BaseTable.Read(stream); + //Default + public static List Read(Stream stream) => BaseList.Read(stream, 99); - public static void Write(Stream stream, IEnumerable items) => - BaseTable.Write(stream, 2, items); + //Override for having a custom amount of entries + public static List Read(Stream stream, int count) => BaseList.Read(stream, count); + + public static void Write(Stream stream, IEnumerable items) => BaseList.Write(stream, items); + + public Lvpm() { } + + public Lvpm(ushort HP, ushort Str, ushort Def, ushort MaxStr, ushort MinStr, ushort Exp) + { + HpMultiplier = HP; + Strength = Str; + Defense = Def; + MaxStrength = MaxStr; + MinStrength = MinStr; + Experience = Exp; + } } } diff --git a/OpenKh.Kh2/Battle/LvpmHelper.cs b/OpenKh.Kh2/Battle/LvpmHelper.cs new file mode 100644 index 000000000..6e8405743 --- /dev/null +++ b/OpenKh.Kh2/Battle/LvpmHelper.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace OpenKh.Kh2.Battle +{ + public class LvpmHelper + { + public ushort Level { get; set; } //Dummy ID to make lvpm listpatchable + public ushort HpMultiplier { get; set; } // (Hp * HpMultiplier + 99) / 100 + public ushort Strength { get; set; } + public ushort Defense { get; set; } + public ushort MaxStrength { get; set; } + public ushort MinStrength { get; set; } + public ushort Experience { get; set; } + + public LvpmHelper() { } + + public LvpmHelper(ushort level, ushort hpMultiplier, ushort strength, ushort defense, ushort maxStrength, ushort minStrength, ushort experience) + { + Level = level; + HpMultiplier = hpMultiplier; + Strength = strength; + Defense = defense; + MaxStrength = maxStrength; + MinStrength = minStrength; + Experience = experience; + } + + public static Lvpm ConvertLvpmHelperToLvpm(LvpmHelper lvl) + { + return new Lvpm(lvl.HpMultiplier, lvl.Strength, lvl.Defense, lvl.MaxStrength, lvl.MinStrength, lvl.Experience); + } + + public static List ConvertLvpmListToHelper(List lvpmList) + { + ushort Id = 0; + var helperList = new List(); + foreach (var lvpm in lvpmList) + { + helperList.Add(new LvpmHelper(Id, lvpm.HpMultiplier, lvpm.Strength, lvpm.Defense, lvpm.MaxStrength, lvpm.MinStrength, lvpm.Experience)); + Id++; + } + return helperList; + } + } +} diff --git a/OpenKh.Patcher/PatcherProcessor.cs b/OpenKh.Patcher/PatcherProcessor.cs index e4407865b..14fb058e9 100644 --- a/OpenKh.Patcher/PatcherProcessor.cs +++ b/OpenKh.Patcher/PatcherProcessor.cs @@ -777,6 +777,19 @@ private static void PatchList(Context context, List sources, Stream s Kh2.Battle.Atkp.Write(stream.SetPosition(0), atkpList); break; + case "lvpm": + var lvpmList = Kh2.Battle.Lvpm.Read(stream); + var moddedLvpm = deserializer.Deserialize>(sourceText); + var helperList = Kh2.Battle.LvpmHelper.ConvertLvpmListToHelper(lvpmList); + + foreach (var level in moddedLvpm) + { + var oldLvpm = helperList.First(x => x.Level == level.Level); + lvpmList[helperList.IndexOf(oldLvpm)] = Kh2.Battle.LvpmHelper.ConvertLvpmHelperToLvpm(level); + } + Kh2.Battle.Lvpm.Write(stream.SetPosition(0), lvpmList); + break; + case "objentry": var objEntryList = Kh2.Objentry.Read(stream).ToDictionary(x => x.ObjectId, x => x); var moddedObjEntry = deserializer.Deserialize>(sourceText); diff --git a/OpenKh.Tests/Patcher/PatcherTests.cs b/OpenKh.Tests/Patcher/PatcherTests.cs index d2e8a9f71..918000fe3 100644 --- a/OpenKh.Tests/Patcher/PatcherTests.cs +++ b/OpenKh.Tests/Patcher/PatcherTests.cs @@ -1870,6 +1870,107 @@ void ListPatchAtkpTest() }); } + [Fact] + void ListPatchLvpmTest() + { + var patcher = new PatcherProcessor(); + var serializer = new Serializer(); + var patch = new Metadata() + { + Assets = new List() + { + new AssetFile() + { + Name = "00battle.bar", + Method = "binarc", + Source = new List() + { + new AssetFile() + { + Name = "lvpm", + Method = "listpatch", + Type = "List", + Source = new List() + { + new AssetFile() + { + Name = "LvpmList.yml", + Type = "lvpm" + } + } + } + } + } + } + }; + + File.Create(Path.Combine(AssetsInputDir, "00battle.bar")).Using(stream => + { + var lvpmEntry = new List() + { + new Kh2.Battle.Lvpm + { + HpMultiplier = 89, + Strength = 77, + Defense = 88, + MaxStrength = 40, + MinStrength = 32, + Experience = 777 + } + }; + + using var lvpmStream = new MemoryStream(); + Kh2.Battle.Lvpm.Write(lvpmStream, lvpmEntry); + Bar.Write(stream, new Bar() { + new Bar.Entry() + { + Name = "lvpm", + Type = Bar.EntryType.List, + Stream = lvpmStream + } + }); + }); + + File.Create(Path.Combine(ModInputDir, "LvpmList.yml")).Using(stream => + { + var writer = new StreamWriter(stream); + + var serializer = new Serializer(); + var moddedLvpm = new List{ + new Kh2.Battle.LvpmHelper + { + Level = 0, + HpMultiplier = 89, + Strength = 77, + Defense = 88, + MaxStrength = 40, + MinStrength = 32, + Experience = 777 + } + }; + writer.Write(serializer.Serialize(moddedLvpm)); + writer.Flush(); + }); + + patcher.Patch(AssetsInputDir, ModOutputDir, patch, ModInputDir, Tests: true); + + AssertFileExists(ModOutputDir, "00battle.bar"); + + File.OpenRead(Path.Combine(ModOutputDir, "00battle.bar")).Using(stream => + { + var binarc = Bar.Read(stream); + var lvpmStream = Kh2.Battle.Lvpm.Read(binarc[0].Stream, 1); + var helperList = Kh2.Battle.LvpmHelper.ConvertLvpmListToHelper(lvpmStream); + Assert.Equal(0, helperList[0].Level); + Assert.Equal(89, helperList[0].HpMultiplier); + Assert.Equal(77, helperList[0].Strength); + Assert.Equal(88, helperList[0].Defense); + Assert.Equal(40, helperList[0].MaxStrength); + Assert.Equal(32, helperList[0].MinStrength); + Assert.Equal(777, helperList[0].Experience); + }); + } + [Fact] public void ListPatchPrztTest() { diff --git a/docs/tool/GUI.ModsManager/creatingMods.md b/docs/tool/GUI.ModsManager/creatingMods.md index aa122be80..0e72034eb 100644 --- a/docs/tool/GUI.ModsManager/creatingMods.md +++ b/docs/tool/GUI.ModsManager/creatingMods.md @@ -24,6 +24,7 @@ This document will focus on teaching you how to create mods using the OpenKH Mod * [fmab](#fmab-source-example) * [enmp](#enmp-source-example) * [fmlv](#fmlv-source-example) + * [lvpm](#lvpm-source-example) * [lvup](#lvup-source-example) * [bons](#bons-source-example) * [atkp](#atkp-source-example) @@ -527,6 +528,17 @@ Final: GrowthAbilityLevel: 1 ``` +### `lvpm` Source Example +``` +- Level: 0 + HpMultiplier: 100 + Strength: 45 + Defense: 26 + MaxStrength: 16 + MinStrength: 5 + Experience: 3212 +``` + ### `lvup` Source Example ``` Sora: