From 2dca9f283b7ddc66624181d7dae523b94c4f8008 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 31 May 2020 15:22:12 -0700 Subject: [PATCH] Allow changing other flags for Recipe List Closes #276 --- NHSE.Core/Save/Files/Personal.cs | 4 +- NHSE.Core/Structures/Misc/RecipeBook.cs | 55 ++++++++ .../Player/RecipeListEditor.Designer.cs | 95 +++++++++++--- .../Subforms/Player/RecipeListEditor.cs | 121 +++++++++++++++--- 4 files changed, 232 insertions(+), 43 deletions(-) create mode 100644 NHSE.Core/Structures/Misc/RecipeBook.cs diff --git a/NHSE.Core/Save/Files/Personal.cs b/NHSE.Core/Save/Files/Personal.cs index 128cbe9..f878f0f 100644 --- a/NHSE.Core/Save/Files/Personal.cs +++ b/NHSE.Core/Save/Files/Personal.cs @@ -106,8 +106,8 @@ public AchievementList Achievements set => value.ToBytes().CopyTo(Data, Offsets.CountAchievement); } - public bool[] GetRecipeList() => ArrayUtil.GitBitFlagArray(Data, Offsets.Recipes, Offsets.MaxRecipeID + 1); - public void SetRecipeList(bool[] value) => ArrayUtil.SetBitFlagArray(Data, Offsets.Recipes, value); + public RecipeBook GetRecipeBook() => new RecipeBook(Data.Slice(Offsets.Recipes, RecipeBook.SIZE)); + public void SetRecipeBook(RecipeBook book) => book.Save(Data, Offsets.Recipes); public short[] GetEventFlagsPlayer() { diff --git a/NHSE.Core/Structures/Misc/RecipeBook.cs b/NHSE.Core/Structures/Misc/RecipeBook.cs new file mode 100644 index 0000000..aa1606f --- /dev/null +++ b/NHSE.Core/Structures/Misc/RecipeBook.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; + +namespace NHSE.Core +{ + public class RecipeBook + { + private const int BitFlagArraySize = 0x100; + private const int BitFlagArrayCount = 4; + public const int SIZE = BitFlagArraySize * BitFlagArrayCount; + public const ushort RecipeCount = BitFlagArraySize * 8; + + private readonly byte[] Data; + public RecipeBook(byte[] data) => Data = data; + + public void Save(byte[] data, int offset) => Data.CopyTo(data, offset); + + public bool GetIsKnown(int recipe) => FlagUtil.GetFlag(Data, 0 * BitFlagArraySize, recipe); + public void SetIsKnown(int recipe, bool value = true) => FlagUtil.SetFlag(Data, 0 * BitFlagArraySize, recipe, value); + + public bool GetIsMade(int recipe) => FlagUtil.GetFlag(Data, 1 * BitFlagArraySize, recipe); + public void SetIsMade(int recipe, bool value = true) => FlagUtil.SetFlag(Data, 1 * BitFlagArraySize, recipe, value); + + public bool GetIsNew(int recipe) => FlagUtil.GetFlag(Data, 2 * BitFlagArraySize, recipe); + public void SetIsNew(int recipe, bool value = true) => FlagUtil.SetFlag(Data, 2 * BitFlagArraySize, recipe, value); + + public bool GetIsFavorite(int recipe) => FlagUtil.GetFlag(Data, 3 * BitFlagArraySize, recipe); + public void SetIsFavorite(int recipe, bool value = true) => FlagUtil.SetFlag(Data, 3 * BitFlagArraySize, recipe, value); + + public void GiveAll(IReadOnlyDictionary recipes) + { + foreach (var entry in recipes) + { + var index = entry.Key; + + bool alreadyHave = GetIsKnown(index); + if (alreadyHave) + continue; + + SetIsKnown(index); + SetIsNew(index); + } + } + + public void ClearAll() + { + for (int i = 0; i < RecipeCount; i++) + { + SetIsKnown(i, false); + SetIsNew(i, false); + SetIsFavorite(i, false); + SetIsFavorite(i, false); + } + } + } +} \ No newline at end of file diff --git a/NHSE.WinForms/Subforms/Player/RecipeListEditor.Designer.cs b/NHSE.WinForms/Subforms/Player/RecipeListEditor.Designer.cs index e4effc3..6d8de85 100644 --- a/NHSE.WinForms/Subforms/Player/RecipeListEditor.Designer.cs +++ b/NHSE.WinForms/Subforms/Player/RecipeListEditor.Designer.cs @@ -30,16 +30,20 @@ private void InitializeComponent() { this.B_Cancel = new System.Windows.Forms.Button(); this.B_Save = new System.Windows.Forms.Button(); - this.CLB_Recipes = new System.Windows.Forms.CheckedListBox(); this.B_All = new System.Windows.Forms.Button(); this.CB_Goto = new System.Windows.Forms.ComboBox(); this.L_Goto = new System.Windows.Forms.Label(); + this.LB_Recipes = new System.Windows.Forms.ListBox(); + this.CHK_Known = new System.Windows.Forms.CheckBox(); + this.CHK_Made = new System.Windows.Forms.CheckBox(); + this.CHK_Favorite = new System.Windows.Forms.CheckBox(); + this.CHK_New = new System.Windows.Forms.CheckBox(); this.SuspendLayout(); // // B_Cancel // this.B_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Cancel.Location = new System.Drawing.Point(130, 326); + this.B_Cancel.Location = new System.Drawing.Point(242, 346); this.B_Cancel.Name = "B_Cancel"; this.B_Cancel.Size = new System.Drawing.Size(72, 23); this.B_Cancel.TabIndex = 7; @@ -50,7 +54,7 @@ private void InitializeComponent() // B_Save // this.B_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.B_Save.Location = new System.Drawing.Point(208, 326); + this.B_Save.Location = new System.Drawing.Point(320, 346); this.B_Save.Name = "B_Save"; this.B_Save.Size = new System.Drawing.Size(72, 23); this.B_Save.TabIndex = 6; @@ -58,22 +62,10 @@ private void InitializeComponent() this.B_Save.UseVisualStyleBackColor = true; this.B_Save.Click += new System.EventHandler(this.B_Save_Click); // - // CLB_Recipes - // - this.CLB_Recipes.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.CLB_Recipes.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.CLB_Recipes.FormattingEnabled = true; - this.CLB_Recipes.Location = new System.Drawing.Point(12, 39); - this.CLB_Recipes.Name = "CLB_Recipes"; - this.CLB_Recipes.Size = new System.Drawing.Size(268, 274); - this.CLB_Recipes.TabIndex = 8; - // // B_All // this.B_All.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.B_All.Location = new System.Drawing.Point(12, 327); + this.B_All.Location = new System.Drawing.Point(12, 347); this.B_All.Name = "B_All"; this.B_All.Size = new System.Drawing.Size(82, 23); this.B_All.TabIndex = 10; @@ -90,7 +82,7 @@ private void InitializeComponent() this.CB_Goto.FormattingEnabled = true; this.CB_Goto.Location = new System.Drawing.Point(78, 12); this.CB_Goto.Name = "CB_Goto"; - this.CB_Goto.Size = new System.Drawing.Size(202, 21); + this.CB_Goto.Size = new System.Drawing.Size(206, 21); this.CB_Goto.TabIndex = 11; this.CB_Goto.SelectedValueChanged += new System.EventHandler(this.CB_Goto_SelectedValueChanged); // @@ -103,15 +95,74 @@ private void InitializeComponent() this.L_Goto.Text = "goto:"; this.L_Goto.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // + // LB_Recipes + // + this.LB_Recipes.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.LB_Recipes.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.LB_Recipes.FormattingEnabled = true; + this.LB_Recipes.ItemHeight = 14; + this.LB_Recipes.Location = new System.Drawing.Point(12, 39); + this.LB_Recipes.Name = "LB_Recipes"; + this.LB_Recipes.Size = new System.Drawing.Size(272, 298); + this.LB_Recipes.TabIndex = 13; + this.LB_Recipes.SelectedIndexChanged += new System.EventHandler(this.LB_Recipes_SelectedIndexChanged); + // + // CHK_Known + // + this.CHK_Known.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.CHK_Known.Location = new System.Drawing.Point(290, 39); + this.CHK_Known.Name = "CHK_Known"; + this.CHK_Known.Size = new System.Drawing.Size(102, 17); + this.CHK_Known.TabIndex = 14; + this.CHK_Known.Text = "Known"; + this.CHK_Known.UseVisualStyleBackColor = true; + this.CHK_Known.CheckedChanged += new System.EventHandler(this.CHK_Known_CheckedChanged); + // + // CHK_Made + // + this.CHK_Made.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.CHK_Made.Location = new System.Drawing.Point(290, 62); + this.CHK_Made.Name = "CHK_Made"; + this.CHK_Made.Size = new System.Drawing.Size(102, 17); + this.CHK_Made.TabIndex = 15; + this.CHK_Made.Text = "Crafted"; + this.CHK_Made.UseVisualStyleBackColor = true; + // + // CHK_Favorite + // + this.CHK_Favorite.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.CHK_Favorite.Location = new System.Drawing.Point(290, 85); + this.CHK_Favorite.Name = "CHK_Favorite"; + this.CHK_Favorite.Size = new System.Drawing.Size(102, 17); + this.CHK_Favorite.TabIndex = 16; + this.CHK_Favorite.Text = "Favorite"; + this.CHK_Favorite.UseVisualStyleBackColor = true; + // + // CHK_New + // + this.CHK_New.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.CHK_New.Location = new System.Drawing.Point(290, 108); + this.CHK_New.Name = "CHK_New"; + this.CHK_New.Size = new System.Drawing.Size(102, 17); + this.CHK_New.TabIndex = 17; + this.CHK_New.Text = "New"; + this.CHK_New.UseVisualStyleBackColor = true; + // // RecipeListEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(292, 361); + this.ClientSize = new System.Drawing.Size(404, 381); + this.Controls.Add(this.CHK_New); + this.Controls.Add(this.CHK_Favorite); + this.Controls.Add(this.CHK_Made); + this.Controls.Add(this.CHK_Known); + this.Controls.Add(this.LB_Recipes); this.Controls.Add(this.L_Goto); this.Controls.Add(this.CB_Goto); this.Controls.Add(this.B_All); - this.Controls.Add(this.CLB_Recipes); this.Controls.Add(this.B_Cancel); this.Controls.Add(this.B_Save); this.Icon = global::NHSE.WinForms.Properties.Resources.icon; @@ -128,9 +179,13 @@ private void InitializeComponent() private System.Windows.Forms.Button B_Cancel; private System.Windows.Forms.Button B_Save; - private System.Windows.Forms.CheckedListBox CLB_Recipes; private System.Windows.Forms.Button B_All; private System.Windows.Forms.ComboBox CB_Goto; private System.Windows.Forms.Label L_Goto; + private System.Windows.Forms.ListBox LB_Recipes; + private System.Windows.Forms.CheckBox CHK_Known; + private System.Windows.Forms.CheckBox CHK_Made; + private System.Windows.Forms.CheckBox CHK_Favorite; + private System.Windows.Forms.CheckBox CHK_New; } } \ No newline at end of file diff --git a/NHSE.WinForms/Subforms/Player/RecipeListEditor.cs b/NHSE.WinForms/Subforms/Player/RecipeListEditor.cs index d4b195e..dd0916b 100644 --- a/NHSE.WinForms/Subforms/Player/RecipeListEditor.cs +++ b/NHSE.WinForms/Subforms/Player/RecipeListEditor.cs @@ -8,16 +8,16 @@ namespace NHSE.WinForms public partial class RecipeListEditor : Form { private readonly Player Player; - private readonly bool[] Known; + private readonly RecipeBook Book; private const string Unknown = "???"; public RecipeListEditor(Player player) { Player = player; - Known = Player.Personal.GetRecipeList(); InitializeComponent(); this.TranslateInterface(GameInfo.CurrentLanguage); + Book = player.Personal.GetRecipeBook(); var gotoSource = FillCheckBoxes(); gotoSource.SortByText(); CB_Goto.DisplayMember = nameof(ComboItem.Text); @@ -27,45 +27,124 @@ public RecipeListEditor(Player player) private List FillCheckBoxes() { + var book = Book; var recipes = RecipeList.Recipes; - var max = (ushort)Known.Length - 1; - var strings = GameInfo.Strings.itemlist; + const ushort max = RecipeBook.RecipeCount; + FillListBox(max, recipes, strings, book); + var gotoSource = new List(); + return GetJumpList(max, recipes, strings, gotoSource); + } + + private void FillListBox(ushort max, IReadOnlyDictionary recipes, string[] strings, RecipeBook book) + { for (ushort i = 0; i <= max; i++) { - bool exists = recipes.TryGetValue(i, out var value); - string item = exists ? strings[value] : Unknown; - if (string.IsNullOrEmpty(item)) - item = value.ToString(); - string name = $"0x{i:X3} - {item}"; - CLB_Recipes.Items.Add(name, Known[i]); - if (exists) - gotoSource.Add(new ComboItem(item, i)); + var name = GetItemName(i, recipes, strings); + var text = GetEntryText(book, i, name); + LB_Recipes.Items.Add(text); } + } + + private static List GetJumpList(ushort max, IReadOnlyDictionary recipes, string[] strings, List gotoSource) + { + for (ushort i = 0; i <= max; i++) + { + var name = GetItemName(i, recipes, strings); + if (recipes.ContainsKey(i)) + gotoSource.Add(new ComboItem(name, i)); + } + return gotoSource; } + private static string GetItemName(ushort index, IReadOnlyDictionary recipes, string[] itemNames) + { + bool exists = recipes.TryGetValue(index, out var value); + string item = exists ? itemNames[value] : Unknown; + if (string.IsNullOrEmpty(item)) + item = value.ToString(); + return item; + } + + private static object GetEntryText(RecipeBook recipeBook, ushort index, string item) + { + var known = recipeBook.GetIsKnown(index) ? "✅" : "❌"; + return $"0x{index:X3} - {known} {item}"; + } + private void B_All_Click(object sender, EventArgs e) { + var index = LB_Recipes.SelectedIndex; + var book = Book; var recipes = RecipeList.Recipes; - for (ushort i = 0; i < CLB_Recipes.Items.Count; i++) - { - bool exists = recipes.ContainsKey(i); - CLB_Recipes.SetItemChecked(i, exists); - } + var strings = GameInfo.Strings.itemlist; + const ushort max = RecipeBook.RecipeCount; + + book.GiveAll(recipes); + LB_Recipes.Items.Clear(); + FillListBox(max, recipes, strings, book); + LB_Recipes.SelectedIndex = index; } private void B_Cancel_Click(object sender, EventArgs e) => Close(); private void B_Save_Click(object sender, EventArgs e) { - for (int i = 0; i < Known.Length; i++) - Known[i] = CLB_Recipes.GetItemChecked(i); - Player.Personal.SetRecipeList(Known); + Player.Personal.SetRecipeBook(Book); Close(); } - private void CB_Goto_SelectedValueChanged(object sender, EventArgs e) => CLB_Recipes.SelectedIndex = WinFormsUtil.GetIndex(CB_Goto); + private void CB_Goto_SelectedValueChanged(object sender, EventArgs e) => LB_Recipes.SelectedIndex = WinFormsUtil.GetIndex(CB_Goto); + + private int Index = -1; + private bool Loading; + private void LB_Recipes_SelectedIndexChanged(object sender, EventArgs e) + { + if (Loading) + return; + SaveIndex(Index); + Index = LB_Recipes.SelectedIndex; + Loading = true; + LoadIndex(Index); + Loading = false; + } + + private void LoadIndex(in int index) + { + if (index < 0) + return; + var book = Book; + CHK_Known.Checked = book.GetIsKnown(index); + CHK_New.Checked = book.GetIsNew(index); + CHK_Made.Checked = book.GetIsMade(index); + CHK_Favorite.Checked = book.GetIsFavorite(index); + } + + private void SaveIndex(in int index) + { + if (index < 0) + return; + var book = Book; + book.SetIsKnown(index, CHK_Known.Checked); + book.SetIsNew(index, CHK_New.Checked); + book.SetIsMade(index, CHK_Made.Checked); + book.SetIsFavorite(index, CHK_Favorite.Checked); + } + + private void CHK_Known_CheckedChanged(object sender, EventArgs e) + { + if (Loading) + return; + Loading = true; + var book = Book; + book.SetIsKnown(Index, CHK_Known.Checked); + var recipes = RecipeList.Recipes; + var strings = GameInfo.Strings.itemlist; + var name = GetItemName((ushort)Index, recipes, strings); + LB_Recipes.Items[Index] = GetEntryText(book, (ushort)Index, name); + Loading = false; + } } }