From ac25835d65597107e85b4838294793634bfb88b3 Mon Sep 17 00:00:00 2001 From: Kurt Date: Mon, 15 Dec 2025 22:33:11 -0600 Subject: [PATCH] Finish Donut struct (0x00 is milliseconds!) Interlink the GUI for ticks+calendar so that modifying one updates the other if applicable. add randomize (very simplistic, just pick a random lv3 power) add all-shiny (sparkling, alpha/big/little, catching) --- .../Substructures/Gen9/ZA/DonutPocket9a.cs | 120 +++++++++++++++++- PKHeX.WinForms/Resources/text/lang_de.txt | 3 + PKHeX.WinForms/Resources/text/lang_en.txt | 3 + PKHeX.WinForms/Resources/text/lang_es-419.txt | 3 + PKHeX.WinForms/Resources/text/lang_es.txt | 3 + PKHeX.WinForms/Resources/text/lang_fr.txt | 3 + PKHeX.WinForms/Resources/text/lang_it.txt | 3 + PKHeX.WinForms/Resources/text/lang_ja.txt | 3 + PKHeX.WinForms/Resources/text/lang_ko.txt | 3 + .../Resources/text/lang_zh-Hans.txt | 3 + .../Resources/text/lang_zh-Hant.txt | 3 + .../Gen9/DonutEditor9a.Designer.cs | 42 +++--- .../Save Editors/Gen9/DonutEditor9a.cs | 90 ++++++++++++- .../Subforms/Save Editors/Gen9/SAV_Donut9a.cs | 40 +++--- 14 files changed, 270 insertions(+), 52 deletions(-) diff --git a/PKHeX.Core/Saves/Substructures/Gen9/ZA/DonutPocket9a.cs b/PKHeX.Core/Saves/Substructures/Gen9/ZA/DonutPocket9a.cs index 416f4a195..20451c80b 100644 --- a/PKHeX.Core/Saves/Substructures/Gen9/ZA/DonutPocket9a.cs +++ b/PKHeX.Core/Saves/Substructures/Gen9/ZA/DonutPocket9a.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; @@ -14,6 +16,116 @@ public Donut9a GetDonut(int index) var slice = Raw.Slice(index * Donut9a.Size, Donut9a.Size); return new Donut9a(slice); } + + public void Compress() + { + // Remove slots where donut is empty, shift entry up. + int writePos = 0; + for (int readPos = 0; readPos < MaxCount; readPos++) + { + var read = GetDonut(readPos); + if (read.IsEmpty) + continue; + if (writePos != readPos) + { + var write = GetDonut(writePos); + read.CopyTo(write); + read.Clear(); // Clear old slot + } + writePos++; + } + } + + private static ReadOnlySpan Template => + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x54, 0x96, 0x00, 0x10, 0x0E, 0x74, 0x0A, + 0x74, 0x0A, 0x74, 0x0A, 0x74, 0x0A, 0x74, 0x0A, 0x74, 0x0A, 0x74, 0x0A, 0x74, 0x0A, 0x74, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xD4, 0xEC, 0xA6, 0x6A, 0x53, 0x37, 0x0D, + 0x2E, 0xA0, 0x7F, 0xD6, 0xA0, 0x1E, 0xCF, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + public void SetAllAsShinyTemplate() + { + var rand = Util.Rand; + var flavorDict = DonutInfo.Flavors.ToDictionary(z => z.Name, z => z.Hash); + + for (int i = 0; i < MaxCount; i++) + { + // Apply template data + var entry = GetDonut(i); + Template.CopyTo(entry.Data); + + // Update donut with data to match what we want. + ApplyShinySizeCatch(entry, rand, flavorDict); + + // Update creation time + ApplyTimestampNow(entry, i); + } + } + + public void SetAllRandomLv3() + { + var rand = Util.Rand; + var flavorDict = DonutInfo.Flavors.Where(z => z.Name.EndsWith("lv3")).ToArray(); + + for (int i = 0; i < MaxCount; i++) + { + // Apply template data + var entry = GetDonut(i); + Template.CopyTo(entry.Data); + + // Update donut with data to match what we want. + rand.Shuffle(flavorDict); + entry.Flavor0 = flavorDict[0].Hash; + entry.Flavor1 = flavorDict[1].Hash; + entry.Flavor2 = flavorDict[2].Hash; + + // Update creation time + ApplyTimestampNow(entry, i); + } + } + + private static void ApplyTimestampNow(Donut9a entry, int bias = 0) + { + var now = DateTime.Now; + if (bias != 0) // some fudge factor to differentiate donuts made on the same millisecond + now = now.AddMilliseconds(bias); + var ticks = (ulong)(now - new DateTime(1900, 1, 1)).TotalMilliseconds; + entry.MillisecondsSince1900 = ticks; + entry.DateTime1900.Timestamp = now; + } + + private static void ApplyShinySizeCatch(Donut9a entry, Random rand, Dictionary flavorDict) + { + // Shiny power is sweet 3-21. Allow "all" rather than just be single type. + var type = rand.Next(17 + 1 + 1); + var flavor0 = $"sweet_{(type + 3):00}_lv3"; // Sparkling (sweet_03-21) + + var roll2 = rand.Next(3); + var flavor1 = roll2 switch + { + 0 or 1 => $"fresh_{(1 + roll2):00}_lv3", // Humungo (fresh_01) or Teensy (fresh_02) + _ => "sweet_01_lv3", // Alpha + }; + var flavor2 = $"fresh_{(type + 4):00}_lv3"; // Catching Power (fresh_04-22) + + // Set flavors to donut + entry.Flavor0 = flavorDict[flavor0]; + entry.Flavor1 = flavorDict[flavor1]; + entry.Flavor2 = flavorDict[flavor2]; + } + + public void CloneAllFromIndex(int index) + { + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, MaxCount - 1); + var source = GetDonut(index); + for (int i = 0; i < MaxCount; i++) + { + if (i != index) + source.CopyTo(GetDonut(i)); + } + } } public static class DonutInfo @@ -451,7 +563,7 @@ public static bool TryGetFlavorName(ulong hash, [NotNullWhen(true)] out string? public Span Data => Raw.Span; /* - 0x00 u64 Unknown + 0x00 u64 MillisecondsSince1900 0x08 u8 Stars 0x09 u8 LevelBoost @@ -475,7 +587,7 @@ public static bool TryGetFlavorName(ulong hash, [NotNullWhen(true)] out string? 0x40 u64 Reserved */ - public ulong Unknown { get => ReadUInt64LittleEndian(Data); set => WriteUInt64LittleEndian(Data, value); } + public ulong MillisecondsSince1900 { get => ReadUInt64LittleEndian(Data); set => WriteUInt64LittleEndian(Data, value); } public byte Stars { get => Data[0x08]; set => Data[0x08] = value; } public byte LevelBoost { get => Data[0x09]; set => Data[0x09] = value; } @@ -505,7 +617,9 @@ public static bool TryGetFlavorName(ulong hash, [NotNullWhen(true)] out string? public ulong Reserved { get => ReadUInt64LittleEndian(Data[0x40..]); set => WriteUInt64LittleEndian(Data[0x40..], value); } - public bool IsEmpty => Unknown != 0; + public void UpdateDateTime() => DateTime1900.Timestamp = new DateTime(1900, 1, 1).AddMilliseconds(MillisecondsSince1900); + + public bool IsEmpty => MillisecondsSince1900 != 0; public int FlavorCount => Flavor0 == 0 ? 0 : Flavor1 == 0 ? 1 : Flavor2 == 0 ? 2 : 3; public ushort[] GetBerries() => diff --git a/PKHeX.WinForms/Resources/text/lang_de.txt b/PKHeX.WinForms/Resources/text/lang_de.txt index 212f68be1..f520b51ff 100644 --- a/PKHeX.WinForms/Resources/text/lang_de.txt +++ b/PKHeX.WinForms/Resources/text/lang_de.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=Pokédex Skin SAV_DLC5.Tab_Pokestar=Pokéwood SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Hinzuf. SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_en.txt b/PKHeX.WinForms/Resources/text/lang_en.txt index 91685c9ee..45df58639 100644 --- a/PKHeX.WinForms/Resources/text/lang_en.txt +++ b/PKHeX.WinForms/Resources/text/lang_en.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Add SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_es-419.txt b/PKHeX.WinForms/Resources/text/lang_es-419.txt index 193d6d771..cd7e7ad46 100644 --- a/PKHeX.WinForms/Resources/text/lang_es-419.txt +++ b/PKHeX.WinForms/Resources/text/lang_es-419.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Add SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_es.txt b/PKHeX.WinForms/Resources/text/lang_es.txt index 2196ee285..3d1fe61fc 100644 --- a/PKHeX.WinForms/Resources/text/lang_es.txt +++ b/PKHeX.WinForms/Resources/text/lang_es.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Add SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_fr.txt b/PKHeX.WinForms/Resources/text/lang_fr.txt index 0cd775b24..2e39141cb 100644 --- a/PKHeX.WinForms/Resources/text/lang_fr.txt +++ b/PKHeX.WinForms/Resources/text/lang_fr.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Ajouter SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_it.txt b/PKHeX.WinForms/Resources/text/lang_it.txt index 3a0876168..cc09fbbcc 100644 --- a/PKHeX.WinForms/Resources/text/lang_it.txt +++ b/PKHeX.WinForms/Resources/text/lang_it.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Add SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_ja.txt b/PKHeX.WinForms/Resources/text/lang_ja.txt index c2c00a312..44872ab34 100644 --- a/PKHeX.WinForms/Resources/text/lang_ja.txt +++ b/PKHeX.WinForms/Resources/text/lang_ja.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=追加 SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_ko.txt b/PKHeX.WinForms/Resources/text/lang_ko.txt index 6c0b1fda9..f8b31f7bb 100644 --- a/PKHeX.WinForms/Resources/text/lang_ko.txt +++ b/PKHeX.WinForms/Resources/text/lang_ko.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Add SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_zh-Hans.txt b/PKHeX.WinForms/Resources/text/lang_zh-Hans.txt index f96d23746..b8768774b 100644 --- a/PKHeX.WinForms/Resources/text/lang_zh-Hans.txt +++ b/PKHeX.WinForms/Resources/text/lang_zh-Hans.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=宝可梦图鉴皮肤 SAV_DLC5.Tab_Pokestar=宝可梦好莱坞 SAV_DLC5.Tab_PWT=宝可梦世界锦标赛 SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=添加 SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Resources/text/lang_zh-Hant.txt b/PKHeX.WinForms/Resources/text/lang_zh-Hant.txt index 60760edfa..ad5aa9915 100644 --- a/PKHeX.WinForms/Resources/text/lang_zh-Hant.txt +++ b/PKHeX.WinForms/Resources/text/lang_zh-Hant.txt @@ -1085,6 +1085,8 @@ SAV_DLC5.Tab_PokeDex=PokéDex Skin SAV_DLC5.Tab_Pokestar=Pokéstar Studios SAV_DLC5.Tab_PWT=PWT SAV_Donut9a.B_Cancel=Cancel +SAV_Donut9a.B_Export=Export +SAV_Donut9a.B_Import=Import SAV_Donut9a.B_ModifyAll=Modify All SAV_Donut9a.B_Reset=Reset SAV_Donut9a.B_Save=Save @@ -1103,6 +1105,7 @@ SAV_Donut9a.L_Flavor0=Flavor 1: SAV_Donut9a.L_Flavor1=Flavor 2: SAV_Donut9a.L_Flavor2=Flavor 3: SAV_Donut9a.L_LevelBoost=Level Boost: +SAV_Donut9a.L_Milliseconds=Milliseconds: SAV_Donut9a.L_Stars=Stars: SAV_Encounters.B_Add=Add SAV_Encounters.B_CriteriaFromTabs=From Editor diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.Designer.cs index 422188fc1..cffa3eb83 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.Designer.cs @@ -46,7 +46,7 @@ private void InitializeComponent() PB_Donut = new System.Windows.Forms.PictureBox(); flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); - TB_Unknown = new System.Windows.Forms.TextBox(); + TB_Milliseconds = new System.Windows.Forms.TextBox(); NUD_Stars = new System.Windows.Forms.NumericUpDown(); L_Donut = new System.Windows.Forms.Label(); NUD_LevelBoost = new System.Windows.Forms.NumericUpDown(); @@ -62,7 +62,7 @@ private void InitializeComponent() NUD_Calories = new System.Windows.Forms.NumericUpDown(); CB_Donut = new System.Windows.Forms.ComboBox(); CAL_Date = new System.Windows.Forms.DateTimePicker(); - L_Unknown = new System.Windows.Forms.Label(); + L_Milliseconds = new System.Windows.Forms.Label(); tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)PB_Berry0).BeginInit(); ((System.ComponentModel.ISupportInitialize)PB_Berry1).BeginInit(); @@ -473,7 +473,7 @@ private void InitializeComponent() tableLayoutPanel2.ColumnCount = 2; tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - tableLayoutPanel2.Controls.Add(TB_Unknown, 1, 8); + tableLayoutPanel2.Controls.Add(TB_Milliseconds, 1, 8); tableLayoutPanel2.Controls.Add(NUD_Stars, 1, 0); tableLayoutPanel2.Controls.Add(L_Donut, 0, 3); tableLayoutPanel2.Controls.Add(NUD_LevelBoost, 1, 2); @@ -489,7 +489,7 @@ private void InitializeComponent() tableLayoutPanel2.Controls.Add(NUD_Calories, 1, 1); tableLayoutPanel2.Controls.Add(CB_Donut, 1, 3); tableLayoutPanel2.Controls.Add(CAL_Date, 1, 7); - tableLayoutPanel2.Controls.Add(L_Unknown, 0, 8); + tableLayoutPanel2.Controls.Add(L_Milliseconds, 0, 8); tableLayoutPanel2.Location = new System.Drawing.Point(296, 0); tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); tableLayoutPanel2.Name = "tableLayoutPanel2"; @@ -507,13 +507,13 @@ private void InitializeComponent() tableLayoutPanel2.Size = new System.Drawing.Size(348, 220); tableLayoutPanel2.TabIndex = 2; // - // TB_Unknown + // TB_Milliseconds // - TB_Unknown.Location = new System.Drawing.Point(106, 194); - TB_Unknown.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); - TB_Unknown.Name = "TB_Unknown"; - TB_Unknown.Size = new System.Drawing.Size(163, 23); - TB_Unknown.TabIndex = 60; + TB_Milliseconds.Location = new System.Drawing.Point(106, 194); + TB_Milliseconds.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); + TB_Milliseconds.Name = "TB_Milliseconds"; + TB_Milliseconds.Size = new System.Drawing.Size(163, 23); + TB_Milliseconds.TabIndex = 60; // // NUD_Stars // @@ -675,16 +675,16 @@ private void InitializeComponent() CAL_Date.TabIndex = 58; CAL_Date.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0); // - // L_Unknown + // L_Milliseconds // - L_Unknown.Anchor = System.Windows.Forms.AnchorStyles.Right; - L_Unknown.Location = new System.Drawing.Point(2, 194); - L_Unknown.Margin = new System.Windows.Forms.Padding(0); - L_Unknown.Name = "L_Unknown"; - L_Unknown.Size = new System.Drawing.Size(104, 24); - L_Unknown.TabIndex = 61; - L_Unknown.Text = "Unknown:"; - L_Unknown.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + L_Milliseconds.Anchor = System.Windows.Forms.AnchorStyles.Right; + L_Milliseconds.Location = new System.Drawing.Point(2, 194); + L_Milliseconds.Margin = new System.Windows.Forms.Padding(0); + L_Milliseconds.Name = "L_Milliseconds"; + L_Milliseconds.Size = new System.Drawing.Size(104, 24); + L_Milliseconds.TabIndex = 61; + L_Milliseconds.Text = "Milliseconds:"; + L_Milliseconds.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // DonutEditor9a // @@ -747,7 +747,7 @@ private void InitializeComponent() private System.Windows.Forms.PictureBox PB_Donut; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; - private System.Windows.Forms.TextBox TB_Unknown; + private System.Windows.Forms.TextBox TB_Milliseconds; private System.Windows.Forms.NumericUpDown NUD_Stars; private System.Windows.Forms.Label L_Donut; private System.Windows.Forms.NumericUpDown NUD_LevelBoost; @@ -763,6 +763,6 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown NUD_Calories; private System.Windows.Forms.ComboBox CB_Donut; private System.Windows.Forms.DateTimePicker CAL_Date; - private System.Windows.Forms.Label L_Unknown; + private System.Windows.Forms.Label L_Milliseconds; } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.cs index 33e0f1f4e..783dcc638 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutEditor9a.cs @@ -12,6 +12,10 @@ public sealed partial class DonutEditor9a : UserControl private Donut9a _donut; public event EventHandler? ValueChanged; + private bool Loading; + + private static readonly DateTime Epoch = new(1900, 1, 1); + public DonutEditor9a() { InitializeComponent(); @@ -57,7 +61,9 @@ public void InitializeLists(ReadOnlySpan flavors, ReadOnlySpan i // Not really necessary to indicate value changes (name wouldn't be different), but for consistency... CAL_Date.ValueChanged += OnValueChanged; - TB_Unknown.TextChanged += OnValueChanged; + CAL_Date.ValueChanged += ChangeDateTime; + TB_Milliseconds.TextChanged += OnValueChanged; + TB_Milliseconds.TextChanged += ChangeMilliseconds; } private static void SetDataSource(ComboBox cb, List list) @@ -116,10 +122,9 @@ private static List GetFlavorText(ReadOnlySpan localized, str return result; } - private static readonly DateTime Epoch = new(1900, 1, 1); - public void LoadDonut(Donut9a donut) { + Loading = true; _donut = donut; LoadClamp(NUD_Stars, donut.Stars); @@ -156,8 +161,9 @@ public void LoadDonut(Donut9a donut) CAL_Date.Value = Epoch; } - TB_Unknown.Text = donut.Unknown.ToString(); + TB_Milliseconds.Text = donut.MillisecondsSince1900.ToString(); + Loading = false; return; static void LoadClamp(NumericUpDown nud, decimal value) => nud.Value = Math.Clamp(value, nud.Minimum, nud.Maximum); @@ -206,7 +212,7 @@ public void SaveDonut() donut.ClearDateTime(); } } - donut.Unknown = ulong.TryParse(TB_Unknown.Text, out var unk) ? unk : 0; + donut.MillisecondsSince1900 = ulong.TryParse(TB_Milliseconds.Text, out var unk) ? unk : 0; } private static void LoadDonutFlavorHash(ComboBox cb, ulong flavorHash) @@ -246,6 +252,80 @@ public void Reset() LoadDonut(_donut); } + private void ChangeMilliseconds(object? sender, EventArgs e) + { + if (Loading) + return; + + if (!ulong.TryParse(TB_Milliseconds.Text, out var ms)) + return; + + try + { + var ticks = Epoch.AddMilliseconds(ms); + + // If date is same, don't update the ticks. + var date = CAL_Date.Value; + if (IsDateEquivalent(date, ticks)) + return; + + Loading = true; + CAL_Date.Value = ticks; + Loading = false; + } + catch + { + // Why are you putting ugly values?? + // Reset. + Loading = true; + CAL_Date.Value = DateTime.Now; + TB_Milliseconds.Text = ((ulong)(CAL_Date.Value - Epoch).TotalMilliseconds).ToString(); + Loading = false; + } + } + + private void ChangeDateTime(object? sender, EventArgs e) + { + if (Loading) + return; + + if (!ulong.TryParse(TB_Milliseconds.Text, out var ms)) + return; + + try + { + var ticks = Epoch.AddMilliseconds(ms); + + // If date is same, don't update the ticks. + var date = CAL_Date.Value; + if (IsDateEquivalent(date, ticks)) + return; + + var delta = ((ulong)(date - Epoch).TotalMilliseconds); + // retain existing ticks _xxx component, since datetime picker does not configure millis + var exist = ms % 1000; + delta -= delta % 1000; + delta += exist; + + Loading = true; + TB_Milliseconds.Text = delta.ToString(); + Loading = false; + } + catch + { + // Why are you putting ugly values?? + // Reset. + Loading = true; + CAL_Date.Value = DateTime.Now; + TB_Milliseconds.Text = ((ulong)(CAL_Date.Value - Epoch).TotalMilliseconds).ToString(); + Loading = false; + } + } + + private static bool IsDateEquivalent(DateTime a, DateTime b) => + a.Year == b.Year && a.Month == b.Month && a.Day == b.Day && + a.Hour == b.Hour && a.Minute == b.Minute && a.Second == b.Second; + // bubble up to the parent control, if subscribed. private void OnValueChanged(object? sender, EventArgs e) => ValueChanged?.Invoke(this, EventArgs.Empty); diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs index 905aa3624..f29272e2d 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs @@ -34,10 +34,6 @@ public SAV_Donut9a(SAV9ZA sav) lastIndex = 0; GetEntry(0); - // Not implemented. - mnuRandomizeMax.Visible = false; - mnuShinyAssortment.Visible = false; - AddDrop(this, LB_Donut, donutEditor); } @@ -60,6 +56,12 @@ private void LoadDonutNames() LB_Donut.Items.AddRange(names); } + private void ReloadDonutNames() + { + for (int i = 0; i < DonutPocket9a.MaxCount; i++) + LB_Donut.Items[i] = GetDonutName(i); + } + private string GetDonutName(int i) { var donut = Donuts.GetDonut(i); @@ -132,34 +134,26 @@ private void B_Modify_Click(object sender, EventArgs e) private void RandomizeAll(object sender, EventArgs e) { - for (int i = 0; i < DonutPocket9a.MaxCount; i++) - { - // todo - } + Donuts.SetAllRandomLv3(); + ReloadDonutNames(); + GetEntry(lastIndex); + System.Media.SystemSounds.Asterisk.Play(); } private void CloneCurrent(object sender, EventArgs e) { SetEntry(lastIndex); - var current = Donuts.GetDonut(lastIndex); - for (int i = 0; i < DonutPocket9a.MaxCount; i++) - { - if (i == lastIndex) - continue; - var target = Donuts.GetDonut(i); - current.CopyTo(target); - LB_Donut.Items[i] = GetDonutName(target, i); // todo: test this to see if it is any bit slow - } + Donuts.CloneAllFromIndex(lastIndex); + ReloadDonutNames(); + System.Media.SystemSounds.Asterisk.Play(); } private void ShinyAssortment(object sender, EventArgs e) { - for (int i = 0; i < DonutPocket9a.MaxCount; i++) - { - var donut = Donuts.GetDonut(i); - // todo: generate a shiny donut - LB_Donut.Items[i] = GetDonutName(donut, i); // todo: test this to see if it is any bit slow - } + Donuts.SetAllAsShinyTemplate(); + ReloadDonutNames(); + GetEntry(lastIndex); + System.Media.SystemSounds.Asterisk.Play(); } private void B_Reset_Click(object sender, EventArgs e) => donutEditor.Reset();