diff --git a/NHSE.Core/Save/Files/Personal.cs b/NHSE.Core/Save/Files/Personal.cs index f878f0f..027a134 100644 --- a/NHSE.Core/Save/Files/Personal.cs +++ b/NHSE.Core/Save/Files/Personal.cs @@ -100,6 +100,12 @@ public IReadOnlyList ItemChest set => BitConverter.GetBytes(value).CopyTo(Data, Offsets.ItemChest + (Offsets.ItemChestCount * Item.SIZE)); } + public IReactionStore Reactions + { + get => Offsets.ReadReactions(Data); + set => Offsets.SetReactions(Data, value); + } + public AchievementList Achievements { get => Data.Slice(Offsets.CountAchievement, AchievementList.SIZE).ToStructure(); diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets.cs b/NHSE.Core/Save/Offsets/PersonalOffsets.cs index bd184e0..796f0fe 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets.cs @@ -63,5 +63,8 @@ public static PersonalOffsets GetOffsets(FileHeaderInfo Info) _ => throw new IndexOutOfRangeException("Unknown revision!" + Environment.NewLine + Info), }; } + + public abstract IReactionStore ReadReactions(byte[] data); + public abstract void SetReactions(byte[] data, IReactionStore value); } } \ No newline at end of file diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets10.cs b/NHSE.Core/Save/Offsets/PersonalOffsets10.cs index 2101cc1..4614551 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets10.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets10.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets10 : PersonalOffsets public override int MaxRecipeID => 0x2A0; public override int MaxRemakeBitFlag => 0x754 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets11.cs b/NHSE.Core/Save/Offsets/PersonalOffsets11.cs index 19e6718..da1b04c 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets11.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets11.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets11 : PersonalOffsets public override int MaxRecipeID => 0x2C8; public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets12.cs b/NHSE.Core/Save/Offsets/PersonalOffsets12.cs index dc336d2..8fbf6cd 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets12.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets12.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets12 : PersonalOffsets public override int MaxRecipeID => 0x2DA; // mermaid stuff public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets13.cs b/NHSE.Core/Save/Offsets/PersonalOffsets13.cs index abaad2c..f566ebf 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets13.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets13.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets13 : PersonalOffsets public override int MaxRecipeID => 0x2DA; public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets14.cs b/NHSE.Core/Save/Offsets/PersonalOffsets14.cs index 791c761..a4fb2c6 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets14.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets14.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets14 : PersonalOffsets public override int MaxRecipeID => 0x2DA; public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets15.cs b/NHSE.Core/Save/Offsets/PersonalOffsets15.cs index 240bb5d..dc2bd37 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets15.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets15.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets15 : PersonalOffsets public override int MaxRecipeID => 0x2E1; public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Save/Offsets/PersonalOffsets16.cs b/NHSE.Core/Save/Offsets/PersonalOffsets16.cs index 7820a35..0c75901 100644 --- a/NHSE.Core/Save/Offsets/PersonalOffsets16.cs +++ b/NHSE.Core/Save/Offsets/PersonalOffsets16.cs @@ -40,5 +40,8 @@ public sealed class PersonalOffsets16 : PersonalOffsets public override int MaxRecipeID => 0x2E1; public override int MaxRemakeBitFlag => 0x7D0 * 32; + + public override IReactionStore ReadReactions(byte[] data) => data.Slice(Manpu, GSavePlayerManpu15.SIZE).ToStructure(); + public override void SetReactions(byte[] data, IReactionStore value) => ((GSavePlayerManpu15)value).ToBytes().CopyTo(data, Manpu); } } diff --git a/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs b/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs index abb03c4..921fcdb 100644 --- a/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs +++ b/NHSE.Core/Structures/Misc/GSavePlayerManpu.cs @@ -4,8 +4,11 @@ #pragma warning disable CS8618, CA1815, CA1819, IDE1006 namespace NHSE.Core { + /// + /// Original Reactions object from release + /// [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct GSavePlayerManpu + public struct GSavePlayerManpu : IReactionStore { public const int SIZE = 0x88; private const int MaxCount = 64; @@ -55,4 +58,68 @@ public bool AddReaction(Reaction react) private int EmptyIndex => Array.FindIndex(ManpuBit, z => z == 0); } + + /// + /// Reactions object revised in v1.5.0 + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct GSavePlayerManpu15 : IReactionStore + { + public const int SIZE = 0x208; + private const int MaxCount = 256; // up from 64 + private const int WheelCount = 8; + + /// + /// List of known Reaction IDs + /// + [field: MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)] + public Reaction[] ManpuBit { get; set; } + + /// + /// Emotions that are currently bound to the Reaction Wheel. + /// + [field: MarshalAs(UnmanagedType.ByValArray, SizeConst = WheelCount)] + public Reaction[] UIList { get; set; } + + /// + /// Flags indicating if an Reaction (at the same index?) is newly learned or not. + /// + [field: MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = MaxCount)] + public bool[] NewFlag { get; set; } + + public void AddMissingReactions() + { + var all = (Reaction[])Enum.GetValues(typeof(Reaction)); + foreach (var react in all) + AddReaction(react); + } + + // returns true if failed + public bool AddReaction(Reaction react) + { + if (react.ToString().StartsWith("UNUSED")) + return true; + + var index = Array.IndexOf(ManpuBit, react); + if (index >= 0) + return false; + + var empty = EmptyIndex; + if (empty < 0) + return true; + ManpuBit[empty] = react; + return false; + } + + private int EmptyIndex => Array.FindIndex(ManpuBit, z => z == 0); + } + + public interface IReactionStore + { + Reaction[] ManpuBit { get; set; } + Reaction[] UIList { get; set; } + bool[] NewFlag { get; set; } + bool AddReaction(Reaction react); + void AddMissingReactions(); + } } diff --git a/NHSE.WinForms/Editor.cs b/NHSE.WinForms/Editor.cs index efa68ac..3ec0fae 100644 --- a/NHSE.WinForms/Editor.cs +++ b/NHSE.WinForms/Editor.cs @@ -270,8 +270,8 @@ private void B_EditPlayerReceivedItems_Click(object sender, EventArgs e) private void B_EditPlayerReactions_Click(object sender, EventArgs e) { - var player = SAV.Players[PlayerIndex].Personal; - using var editor = new ReactionEditor(player); + var player = SAV.Players[PlayerIndex]; + using var editor = new ReactionEditor(player.Personal); editor.ShowDialog(); } diff --git a/NHSE.WinForms/Subforms/Player/ReactionEditor.cs b/NHSE.WinForms/Subforms/Player/ReactionEditor.cs index a228ff6..35b18a0 100644 --- a/NHSE.WinForms/Subforms/Player/ReactionEditor.cs +++ b/NHSE.WinForms/Subforms/Player/ReactionEditor.cs @@ -14,7 +14,7 @@ public ReactionEditor(Personal p) this.TranslateInterface(GameInfo.CurrentLanguage); Personal = p; - var manpu = p.Data.Slice(p.Offsets.Manpu, GSavePlayerManpu.SIZE).ToStructure(); + var manpu = p.Reactions; PG_Manpu.SelectedObject = manpu; } @@ -24,15 +24,14 @@ public ReactionEditor(Personal p) private void B_Save_Click(object sender, EventArgs e) { DialogResult = DialogResult.OK; - var val = (GSavePlayerManpu) PG_Manpu.SelectedObject; - var bytes = val.ToBytes(); - bytes.CopyTo(Personal.Data, Personal.Offsets.Manpu); + var val = (IReactionStore) PG_Manpu.SelectedObject; + Personal.Reactions = val; Close(); } private void B_GiveAll_Click(object sender, EventArgs e) { - var val = (GSavePlayerManpu)PG_Manpu.SelectedObject; + var val = (IReactionStore)PG_Manpu.SelectedObject; val.AddMissingReactions(); PG_Manpu.SelectedObject = val; System.Media.SystemSounds.Asterisk.Play();