diff --git a/PKHeX.Drawing.Misc/Properties/Resources.Designer.cs b/PKHeX.Drawing.Misc/Properties/Resources.Designer.cs index 1e99f9485..6ef18e4d6 100644 --- a/PKHeX.Drawing.Misc/Properties/Resources.Designer.cs +++ b/PKHeX.Drawing.Misc/Properties/Resources.Designer.cs @@ -3200,6 +3200,16 @@ public class Resources { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + public static System.Drawing.Bitmap flavorprofile { + get { + object obj = ResourceManager.GetObject("flavorprofile", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/PKHeX.Drawing.Misc/Properties/Resources.resx b/PKHeX.Drawing.Misc/Properties/Resources.resx index 6eaae3eb6..37d49aa7f 100644 --- a/PKHeX.Drawing.Misc/Properties/Resources.resx +++ b/PKHeX.Drawing.Misc/Properties/Resources.resx @@ -2665,4 +2665,7 @@ ..\Resources\img\flavor power\bitter_01_lv3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\img\misc\flavorprofile.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/PKHeX.Drawing.Misc/Resources/img/misc/flavorprofile.png b/PKHeX.Drawing.Misc/Resources/img/misc/flavorprofile.png new file mode 100644 index 000000000..36680f66d Binary files /dev/null and b/PKHeX.Drawing.Misc/Resources/img/misc/flavorprofile.png differ diff --git a/PKHeX.Drawing.Misc/Util/DonutSpriteUtil.cs b/PKHeX.Drawing.Misc/Util/DonutSpriteUtil.cs index 80ed687cb..0d2022178 100644 --- a/PKHeX.Drawing.Misc/Util/DonutSpriteUtil.cs +++ b/PKHeX.Drawing.Misc/Util/DonutSpriteUtil.cs @@ -20,6 +20,7 @@ public static class DonutSpriteUtil public static Bitmap? Sprite(this Donut9a donut) => GetDonutImage(donut); public static Bitmap? StarSprite => (Bitmap?)Resources.ResourceManager.GetObject("star"); public static Bitmap? GetDonutFlavorImage(string donut) => (Bitmap?)Resources.ResourceManager.GetObject(donut); + public static Bitmap? GetFlavorProfileImage() => (Bitmap?)Resources.ResourceManager.GetObject("flavorprofile"); private static Bitmap? GetDonutImage(Donut9a donut) { diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutFlavorProfile9a.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutFlavorProfile9a.Designer.cs new file mode 100644 index 000000000..353348a16 --- /dev/null +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutFlavorProfile9a.Designer.cs @@ -0,0 +1,191 @@ +using System.Drawing; + +namespace PKHeX.WinForms +{ + partial class DonutFlavorProfile9a + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + L_Spicy = new System.Windows.Forms.Label(); + L_Sour = new System.Windows.Forms.Label(); + L_Fresh = new System.Windows.Forms.Label(); + L_Bitter = new System.Windows.Forms.Label(); + L_Sweet = new System.Windows.Forms.Label(); + L_SpicyStat = new System.Windows.Forms.Label(); + L_SourStat = new System.Windows.Forms.Label(); + L_FreshStat = new System.Windows.Forms.Label(); + L_BitterStat = new System.Windows.Forms.Label(); + L_SweetStat = new System.Windows.Forms.Label(); + SuspendLayout(); + // + // L_Spicy + // + L_Spicy.AutoSize = true; + L_Spicy.Font = new Font("Segoe UI", 10.8F, FontStyle.Bold); + L_Spicy.ForeColor = Color.FromArgb(255, 192, 192); + L_Spicy.Location = new Point(117, 18); + L_Spicy.Name = "L_Spicy"; + L_Spicy.Size = new Size(45, 20); + L_Spicy.TabIndex = 0; + L_Spicy.Text = "Spicy"; + // + // L_Sour + // + L_Sour.AutoSize = true; + L_Sour.Font = new Font("Segoe UI", 10.8F, FontStyle.Bold); + L_Sour.ForeColor = Color.FromArgb(255, 224, 192); + L_Sour.Location = new Point(197, 71); + L_Sour.Name = "L_Sour"; + L_Sour.Size = new Size(41, 20); + L_Sour.TabIndex = 1; + L_Sour.Text = "Sour"; + // + // L_Fresh + // + L_Fresh.AutoSize = true; + L_Fresh.Font = new Font("Segoe UI", 10.8F, FontStyle.Bold); + L_Fresh.ForeColor = Color.FromArgb(192, 255, 192); + L_Fresh.Location = new Point(181, 143); + L_Fresh.Name = "L_Fresh"; + L_Fresh.Size = new Size(47, 20); + L_Fresh.TabIndex = 2; + L_Fresh.Text = "Fresh"; + // + // L_Bitter + // + L_Bitter.AutoSize = true; + L_Bitter.Font = new Font("Segoe UI", 10.8F, FontStyle.Bold); + L_Bitter.ForeColor = Color.FromArgb(192, 192, 255); + L_Bitter.Location = new Point(47, 143); + L_Bitter.Name = "L_Bitter"; + L_Bitter.Size = new Size(49, 20); + L_Bitter.TabIndex = 3; + L_Bitter.Text = "Bitter"; + // + // L_Sweet + // + L_Sweet.AutoSize = true; + L_Sweet.Font = new Font("Segoe UI", 10.8F, FontStyle.Bold); + L_Sweet.ForeColor = Color.FromArgb(255, 192, 255); + L_Sweet.Location = new Point(37, 71); + L_Sweet.Name = "L_Sweet"; + L_Sweet.Size = new Size(51, 20); + L_Sweet.TabIndex = 4; + L_Sweet.Text = "Sweet"; + // + // L_SpicyStat + // + L_SpicyStat.AutoSize = true; + L_SpicyStat.Font = new Font("Segoe UI", 13.8F, FontStyle.Bold); + L_SpicyStat.ForeColor = Color.White; + L_SpicyStat.Location = new Point(117, 36); + L_SpicyStat.Name = "L_SpicyStat"; + L_SpicyStat.Size = new Size(45, 25); + L_SpicyStat.TabIndex = 5; + L_SpicyStat.Text = "760"; + // + // L_SourStat + // + L_SourStat.AutoSize = true; + L_SourStat.Font = new Font("Segoe UI", 13.8F, FontStyle.Bold); + L_SourStat.ForeColor = Color.White; + L_SourStat.Location = new Point(195, 88); + L_SourStat.Name = "L_SourStat"; + L_SourStat.Size = new Size(45, 25); + L_SourStat.TabIndex = 6; + L_SourStat.Text = "760"; + // + // L_FreshStat + // + L_FreshStat.AutoSize = true; + L_FreshStat.Font = new Font("Segoe UI", 13.8F, FontStyle.Bold); + L_FreshStat.ForeColor = Color.White; + L_FreshStat.Location = new Point(181, 160); + L_FreshStat.Name = "L_FreshStat"; + L_FreshStat.Size = new Size(45, 25); + L_FreshStat.TabIndex = 7; + L_FreshStat.Text = "760"; + // + // L_BitterStat + // + L_BitterStat.AutoSize = true; + L_BitterStat.Font = new Font("Segoe UI", 13.8F, FontStyle.Bold); + L_BitterStat.ForeColor = Color.White; + L_BitterStat.Location = new Point(49, 160); + L_BitterStat.Name = "L_BitterStat"; + L_BitterStat.Size = new Size(45, 25); + L_BitterStat.TabIndex = 8; + L_BitterStat.Text = "760"; + // + // L_SweetStat + // + L_SweetStat.AutoSize = true; + L_SweetStat.Font = new Font("Segoe UI", 13.8F, FontStyle.Bold); + L_SweetStat.ForeColor = Color.White; + L_SweetStat.Location = new Point(40, 88); + L_SweetStat.Name = "L_SweetStat"; + L_SweetStat.Size = new Size(45, 25); + L_SweetStat.TabIndex = 9; + L_SweetStat.Text = "760"; + // + // DonutFlavorProfile9a + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + BackColor = Color.Transparent; + BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; + Controls.Add(L_Sour); + Controls.Add(L_Sweet); + Controls.Add(L_Spicy); + Controls.Add(L_SweetStat); + Controls.Add(L_BitterStat); + Controls.Add(L_FreshStat); + Controls.Add(L_SourStat); + Controls.Add(L_SpicyStat); + Controls.Add(L_Bitter); + Controls.Add(L_Fresh); + DoubleBuffered = true; + Name = "DonutFlavorProfile9a"; + Size = new Size(277, 237); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private System.Windows.Forms.Label L_Spicy; + private System.Windows.Forms.Label L_Sour; + private System.Windows.Forms.Label L_Fresh; + private System.Windows.Forms.Label L_Bitter; + private System.Windows.Forms.Label L_Sweet; + private System.Windows.Forms.Label L_SpicyStat; + private System.Windows.Forms.Label L_SourStat; + private System.Windows.Forms.Label L_FreshStat; + private System.Windows.Forms.Label L_BitterStat; + private System.Windows.Forms.Label L_SweetStat; + } +} diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutFlavorProfile9a.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutFlavorProfile9a.cs new file mode 100644 index 000000000..494c872ad --- /dev/null +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/DonutFlavorProfile9a.cs @@ -0,0 +1,127 @@ +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using PKHeX.Core; + +namespace PKHeX.WinForms; + +public partial class DonutFlavorProfile9a : UserControl +{ + private const int MaxStatValue = 760; + private const int PentagonWidth = 110; + private const int PentagonHeight = 110; + + private int ScaleValue; + + private readonly int[] FlavorProfileStats = new int[5]; + + public DonutFlavorProfile9a() => InitializeComponent(); + + /// + /// Load stats from a Donut9a object + /// + public void LoadFromDonut(Donut9a donut) + { + Span flavorStats = stackalloc int[5]; + donut.RecalculateDonutFlavors(flavorStats); + + flavorStats.CopyTo(FlavorProfileStats); + FlavorProfileStats[0] = flavorStats[0]; // Spicy - top + FlavorProfileStats[1] = flavorStats[4]; // Sour - top-right + FlavorProfileStats[2] = flavorStats[1]; // Fresh - bottom-right + FlavorProfileStats[3] = flavorStats[3]; // Bitter - bottom-left + FlavorProfileStats[4] = flavorStats[2]; // Sweet - top-left + + int maxStat = 0; + foreach (var stat in FlavorProfileStats) + if (stat > maxStat) maxStat = stat; + + ScaleValue = maxStat < 100 ? 500 : Math.Min(((maxStat + 99) / 100) * 100 + 100, MaxStatValue); + + UpdateStatLabels(flavorStats); + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + var g = e.Graphics; + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; + + // Calculate pentagon area - centered in the control + var center = Size / 2; + + // Scale pentagon size relative to control size + float scaleX = Width / 276f; + float scaleY = Height / 240f; + float radiusX = (PentagonWidth / 2f) * scaleX; + float radiusY = (PentagonHeight / 2f) * scaleY; + + // Draw filled pentagon based on stats + DrawStatsPentagon(g, center, radiusX, radiusY); + } + + private void DrawStatsPentagon(Graphics g, Size center, float radiusX, float radiusY) + { + // Calculate scaled points based on stat values with individual max per stat + Span calculatedPoints = stackalloc PointF[5]; + for (int i = 0; i < 5; i++) + { + // Calculate individual max for this stat + var point = GetStatCoordinate(radiusX, radiusY, FlavorProfileStats[i], i); + point += center; + calculatedPoints[i] = point; + } + + // Reorder points for counter-clockwise drawing + Span statPoints = stackalloc PointF[5]; + ReorderCounterClockwise(statPoints, calculatedPoints); + + // Fill the pentagon with semi-transparent yellow + using Brush fillBrush = new SolidBrush(Color.Yellow); + g.FillPolygon(fillBrush, statPoints); + } + + private static PointF GetStatCoordinate(float radiusX, float radiusY, int statValue, int i) + { + int statMax = statValue switch + { + <= 350 => statValue + 200, + <= 700 => ((statValue + 99) / 100) * 100, + _ => MaxStatValue, + }; + + float scale = statMax > 0 ? Math.Min((float)statValue / statMax, 1.0f) : 0f; + + // Use baseline scale (10%) if stat is 0, otherwise use calculated scale + const float baselineScale = 0.10f; + if (scale == 0f) + scale = baselineScale; + + double angleStep = 2 * Math.PI / 5; + double angle = -Math.PI / 2 + i * angleStep; + + float scaledRadiusX = radiusX * scale; + float scaledRadiusY = radiusY * scale; + return new PointF((float)(scaledRadiusX * Math.Cos(angle)), (float)(scaledRadiusY * Math.Sin(angle))); + } + + private static void ReorderCounterClockwise(Span statPoints, ReadOnlySpan calculatedPoints) + { + ReadOnlySpan counterClockwiseOrder = [0, 4, 3, 2, 1]; + for (int i = 0; i < 5; i++) + statPoints[i] = calculatedPoints[counterClockwiseOrder[i]]; + } + + private void UpdateStatLabels(ReadOnlySpan flavorStats) + { + L_SpicyStat.Text = flavorStats[0].ToString(); + L_FreshStat.Text = flavorStats[1].ToString(); + L_SweetStat.Text = flavorStats[2].ToString(); + L_BitterStat.Text = flavorStats[3].ToString(); + L_SourStat.Text = flavorStats[4].ToString(); + } +} diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.Designer.cs index 75fb7d217..00f1bbc21 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.Designer.cs @@ -41,13 +41,14 @@ private void InitializeComponent() donutEditor = new DonutEditor9a(); B_Import = new System.Windows.Forms.Button(); B_Export = new System.Windows.Forms.Button(); + DonutFlavorProfile = new DonutFlavorProfile9a(); modifyMenu.SuspendLayout(); SuspendLayout(); // // B_Cancel // B_Cancel.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - B_Cancel.Location = new System.Drawing.Point(786, 348); + B_Cancel.Location = new System.Drawing.Point(786, 442); B_Cancel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Cancel.Name = "B_Cancel"; B_Cancel.Size = new System.Drawing.Size(93, 27); @@ -64,14 +65,14 @@ private void InitializeComponent() LB_Donut.Location = new System.Drawing.Point(14, 15); LB_Donut.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); LB_Donut.Name = "LB_Donut"; - LB_Donut.Size = new System.Drawing.Size(186, 361); + LB_Donut.Size = new System.Drawing.Size(186, 412); LB_Donut.TabIndex = 2; LB_Donut.SelectedIndexChanged += ChangeIndex; // // B_Save // B_Save.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right; - B_Save.Location = new System.Drawing.Point(786, 319); + B_Save.Location = new System.Drawing.Point(786, 413); B_Save.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Save.Name = "B_Save"; B_Save.Size = new System.Drawing.Size(93, 27); @@ -111,7 +112,7 @@ private void InitializeComponent() // B_ModifyAll // B_ModifyAll.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - B_ModifyAll.Location = new System.Drawing.Point(208, 349); + B_ModifyAll.Location = new System.Drawing.Point(208, 443); B_ModifyAll.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_ModifyAll.Name = "B_ModifyAll"; B_ModifyAll.Size = new System.Drawing.Size(128, 27); @@ -123,7 +124,7 @@ private void InitializeComponent() // B_Reset // B_Reset.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - B_Reset.Location = new System.Drawing.Point(548, 304); + B_Reset.Location = new System.Drawing.Point(344, 443); B_Reset.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Reset.Name = "B_Reset"; B_Reset.Size = new System.Drawing.Size(128, 27); @@ -141,13 +142,13 @@ private void InitializeComponent() donutEditor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; donutEditor.Location = new System.Drawing.Point(207, 15); donutEditor.Name = "donutEditor"; - donutEditor.Size = new System.Drawing.Size(646, 240); + donutEditor.Size = new System.Drawing.Size(647, 240); donutEditor.TabIndex = 27; // // B_Import // B_Import.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - B_Import.Location = new System.Drawing.Point(208, 304); + B_Import.Location = new System.Drawing.Point(208, 398); B_Import.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Import.Name = "B_Import"; B_Import.Size = new System.Drawing.Size(128, 27); @@ -159,7 +160,7 @@ private void InitializeComponent() // B_Export // B_Export.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left; - B_Export.Location = new System.Drawing.Point(344, 304); + B_Export.Location = new System.Drawing.Point(344, 398); B_Export.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); B_Export.Name = "B_Export"; B_Export.Size = new System.Drawing.Size(128, 27); @@ -168,11 +169,20 @@ private void InitializeComponent() B_Export.UseVisualStyleBackColor = true; B_Export.Click += B_Export_Click; // + // DonutFlavorProfile + // + DonutFlavorProfile.BackColor = System.Drawing.Color.Transparent; + DonutFlavorProfile.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; + DonutFlavorProfile.Location = new System.Drawing.Point(502, 243); + DonutFlavorProfile.Name = "DonutFlavorProfile"; + DonutFlavorProfile.Size = new System.Drawing.Size(277, 237); + DonutFlavorProfile.TabIndex = 30; + // // SAV_Donut9a // AllowDrop = true; AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit; - ClientSize = new System.Drawing.Size(894, 388); + ClientSize = new System.Drawing.Size(894, 482); Controls.Add(B_Export); Controls.Add(B_Import); Controls.Add(donutEditor); @@ -181,6 +191,7 @@ private void InitializeComponent() Controls.Add(B_Save); Controls.Add(LB_Donut); Controls.Add(B_Cancel); + Controls.Add(DonutFlavorProfile); FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; Icon = Properties.Resources.Icon; Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); @@ -208,5 +219,6 @@ private void InitializeComponent() private DonutEditor9a donutEditor; private System.Windows.Forms.Button B_Import; private System.Windows.Forms.Button B_Export; + private DonutFlavorProfile9a DonutFlavorProfile; } } diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs index f29272e2d..8f2da0b9c 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen9/SAV_Donut9a.cs @@ -3,6 +3,7 @@ using System.Text; using System.Windows.Forms; using PKHeX.Core; +using PKHeX.Drawing.Misc; namespace PKHeX.WinForms; @@ -35,6 +36,8 @@ public SAV_Donut9a(SAV9ZA sav) GetEntry(0); AddDrop(this, LB_Donut, donutEditor); + + DonutFlavorProfile.BackgroundImage = DonutSpriteUtil.GetFlavorProfileImage(); } private void AddDrop(params ReadOnlySpan objects) @@ -81,12 +84,17 @@ private void Editor_ValueChanged(object? sender, EventArgs e) return; Loading = true; - // Only refresh the name in the list if it has changed. var index = lastIndex; - var currentName = GetDonutName(index); + var donut = Donuts.GetDonut(index); + // Only refresh the name in the list if it has changed. + var currentName = GetDonutName(donut, index); var existing = LB_Donut.Items[index]; if (existing.ToString() != currentName) LB_Donut.Items[index] = currentName; + + // Update profile if applicable + donutEditor.SaveDonut(); + DonutFlavorProfile.LoadFromDonut(donut); Loading = false; } @@ -105,8 +113,11 @@ private void GetEntry(int index) if (Loading || index < 0) return; + Loading = true; var donut = Donuts.GetDonut(index); donutEditor.LoadDonut(donut); + DonutFlavorProfile.LoadFromDonut(donut); + Loading = false; } private void SetEntry(int index)