PKHeX/PKHeX.WinForms/Controls/PKM Editor/StatEditor.cs
Kurt 294a61c4c1 Update pkmeditor GB gui loop behavior
updating HP causes SPD to be updated, which is based on SPA; since SPA
is yet to be updated, we trigger constant updates on an SPA > 15 until
we crash.

just use the SPD IV which is already capped by the pkmdata
(this shouldn't refresh fields anyway when loading... whatever).

Closes #1914
2018-04-25 20:17:58 -07:00

440 lines
16 KiB
C#

using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using PKHeX.Core;
namespace PKHeX.WinForms.Controls
{
public partial class StatEditor : UserControl
{
public StatEditor()
{
InitializeComponent();
MT_EVs = new[] {TB_HPEV, TB_ATKEV, TB_DEFEV, TB_SPEEV, TB_SPAEV, TB_SPDEV};
MT_IVs = new[] {TB_HPIV, TB_ATKIV, TB_DEFIV, TB_SPEIV, TB_SPAIV, TB_SPDIV};
MT_Stats = new[] {Stat_HP, Stat_ATK, Stat_DEF, Stat_SPE, Stat_SPA, Stat_SPD};
L_Stats = new[] {Label_HP, Label_ATK, Label_DEF, Label_SPE, Label_SPA, Label_SPD};
MT_Base = new[] {TB_HPBase, TB_ATKBase, TB_DEFBase, TB_SPEBase, TB_SPABase, TB_SPDBase};
TB_BST.ResetForeColor();
TB_IVTotal.ForeColor = MT_EVs[0].ForeColor;
TB_EVTotal.ForeColor = MT_EVs[0].ForeColor;
}
private IMainEditor MainEditor { get; set; }
public void SetMainEditor(IMainEditor editor)
{
MainEditor = editor;
CHK_HackedStats.Enabled = CHK_HackedStats.Visible = editor.HaX;
}
public bool Valid => pkm.Format < 3 || Convert.ToUInt32(TB_EVTotal.Text) <= 510 || CHK_HackedStats.Checked;
private readonly Label[] L_Stats;
private readonly MaskedTextBox[] MT_EVs, MT_IVs, MT_Stats, MT_Base;
private readonly ToolTip EVTip = new ToolTip();
private PKM pkm => MainEditor.pkm;
private bool[] HTs => new[] {pkm.HT_HP, pkm.HT_ATK, pkm.HT_DEF, pkm.HT_SPE, pkm.HT_SPA, pkm.HT_SPD};
private bool ChangingFields
{
get => MainEditor.ChangingFields;
set => MainEditor.ChangingFields = value;
}
private bool FieldsInitialized => MainEditor.FieldsInitialized;
private bool AllowHyperTrain => pkm.Format >= 7;
private void ClickIV(object sender, EventArgs e)
{
if (!(sender is MaskedTextBox t))
return;
if (ModifierKeys.HasFlag(Keys.Alt)) // Min
t.Text = 0.ToString();
else if (ModifierKeys.HasFlag(Keys.Control))
{
var index = Array.IndexOf(MT_IVs, t);
t.Text = pkm.GetMaximumIV(index, true).ToString();
}
else if (AllowHyperTrain && ModifierKeys.HasFlag(Keys.Shift))
{
var index = Array.IndexOf(MT_IVs, t);
bool flag = pkm.HyperTrainInvert(index);
UpdateHyperTrainingFlag(index, flag);
UpdateStats();
}
}
private void ClickEV(object sender, EventArgs e)
{
if (!(sender is MaskedTextBox t))
return;
if (!ModifierKeys.HasFlag(Keys.Control)) // Max
{
if (ModifierKeys.HasFlag(Keys.Alt)) // Min
t.Text = 0.ToString();
return;
}
int index = Array.IndexOf(MT_EVs, t);
int newEV = pkm.GetMaximumEV(index);
t.Text = newEV.ToString();
}
public void UpdateIVs(object sender, EventArgs e)
{
if (sender is MaskedTextBox m)
{
int value = Util.ToInt32(m.Text);
if (value > pkm.MaxIV)
{
m.Text = pkm.MaxIV.ToString();
return; // recursive on text set
}
int index = Array.IndexOf(MT_IVs, m);
pkm.SetIV(index, value);
}
RefreshDerivedValues(e);
UpdateStats();
}
private void RefreshDerivedValues(object sender)
{
if (pkm.Format < 3)
{
TB_HPIV.Text = pkm.IV_HP.ToString();
TB_SPDIV.Text = pkm.IV_SPD.ToString();
MainEditor.UpdateIVsGB(sender == null);
}
if (!ChangingFields)
{
ChangingFields = true;
CB_HPType.SelectedValue = pkm.HPType;
ChangingFields = false;
}
// Potential Reading
L_Potential.Text = pkm.GetPotentialString(MainEditor.Unicode);
TB_IVTotal.Text = pkm.IVTotal.ToString();
UpdateCharacteristic(pkm.Characteristic);
}
private void UpdateEVs(object sender, EventArgs e)
{
if (sender is MaskedTextBox m)
{
int value = Util.ToInt32(m.Text);
if (value > pkm.MaxEV)
{
m.Text = pkm.MaxEV.ToString();
return; // recursive on text set
}
int index = Array.IndexOf(MT_EVs, m);
pkm.SetEV(index, value);
}
UpdateEVTotals();
if (pkm.Format < 3)
{
ChangingFields = true;
TB_SPDEV.Text = TB_SPAEV.Text;
ChangingFields = false;
}
UpdateStats();
}
private void UpdateRandomEVs(object sender, EventArgs e)
{
bool zero = ModifierKeys.HasFlag(Keys.Control);
var evs = zero ? new int[6] : PKX.GetRandomEVs(pkm.Format);
LoadEVs(evs);
UpdateEVs(null, null);
}
private void UpdateHackedStats(object sender, EventArgs e)
{
foreach (var s in MT_Stats)
s.Enabled = CHK_HackedStats.Checked;
if (!CHK_HackedStats.Checked)
UpdateStats();
}
private void UpdateHackedStatText(object sender, EventArgs e)
{
if (!CHK_HackedStats.Checked || !(sender is TextBox tb))
return;
string text = tb.Text;
if (string.IsNullOrWhiteSpace(text))
tb.Text = "0";
if (Convert.ToUInt32(text) > ushort.MaxValue)
tb.Text = "65535";
}
private void UpdateHyperTrainingFlag(int i, bool value)
{
if (value)
MT_IVs[i].BackColor = Color.LightGreen;
else
MT_IVs[i].ResetBackColor();
}
private void UpdateHPType(object sender, EventArgs e)
{
if (ChangingFields || !FieldsInitialized)
return;
// Change IVs to match the new Hidden Power
int hpower = WinFormsUtil.GetIndex(CB_HPType);
int[] newIVs = PKX.SetHPIVs(hpower, pkm.IVs);
LoadIVs(newIVs);
}
private void ClickStatLabel(object sender, MouseEventArgs e)
{
if (sender == Label_SPC)
sender = Label_SPA;
int index = Array.IndexOf(L_Stats, sender as Label);
if (ModifierKeys.HasFlag(Keys.Alt)) // EV
{
var value = e.Button != MouseButtons.Left ? 0 : pkm.GetMaximumEV(index);
MT_EVs[index].Text = value.ToString();
}
else if (ModifierKeys.HasFlag(Keys.Control)) // IV
{
var value = e.Button != MouseButtons.Left ? 0 : pkm.GetMaximumIV(index, true);
MT_IVs[index].Text = value.ToString();
}
}
private void LoadHyperTraining()
{
var HT_Vals = HTs;
for (int i = 0; i < MT_IVs.Length; i++)
UpdateHyperTrainingFlag(i, HT_Vals[i]);
}
private void UpdateEVTotals()
{
int evtotal = pkm.EVTotal;
if (evtotal > 510) // Background turns Red
TB_EVTotal.BackColor = Color.Red;
else if (evtotal == 510) // Maximum EVs
TB_EVTotal.BackColor = Color.Honeydew;
else if (evtotal == 508) // Fishy EVs
TB_EVTotal.BackColor = Color.LightYellow;
else TB_EVTotal.BackColor = TB_IVTotal.BackColor;
TB_EVTotal.Text = evtotal.ToString();
EVTip.SetToolTip(TB_EVTotal, $"Remaining: {510 - evtotal}");
}
public void UpdateStats()
{
// Generate the stats.
if (!CHK_HackedStats.Checked || pkm.Stat_HPCurrent == 0) // no stats when initially loaded from non-partyformat slot
{
var pi = pkm.PersonalInfo;
pkm.SetStats(pkm.GetStats(pi));
LoadBST(pi);
LoadPartyStats(pkm);
}
RecolorStatLabels(pkm.Nature);
}
private void LoadBST(PersonalInfo pi)
{
var stats = new[] {pi.HP, pi.ATK, pi.DEF, pi.SPE, pi.SPA, pi.SPD};
for (int i = 0; i < stats.Length; i++)
{
MT_Base[i].Text = stats[i].ToString("000");
MT_Base[i].BackColor = MapColor(stats[i]);
}
var bst = pi.BST;
TB_BST.Text = bst.ToString("000");
TB_BST.BackColor = MapColor((int)(Math.Max(0, bst - 175) / 3f));
}
private static Color MapColor(int v)
{
const float maxval = 180; // shift the green cap down
float x = 100f * v / maxval;
if (x > 100)
x = 100;
double red = 255f * (x > 50 ? 1 - 2 * (x - 50) / 100.0 : 1.0);
double green = 255f * (x > 50 ? 1.0 : 2 * x / 100.0);
return Blend(Color.FromArgb((int)red, (int)green, 0), Color.White, 0.4);
}
private static Color Blend(Color color, Color backColor, double amount)
{
byte r = (byte)(color.R * amount + backColor.R * (1 - amount));
byte g = (byte)(color.G * amount + backColor.G * (1 - amount));
byte b = (byte)(color.B * amount + backColor.B * (1 - amount));
return Color.FromArgb(r, g, b);
}
public void UpdateRandomIVs(object sender, EventArgs e)
{
ChangingFields = true;
if (ModifierKeys.HasFlag(Keys.Control)) // Max IVs
{
int[] IVs = { pkm.MaxIV, pkm.MaxIV, pkm.MaxIV, pkm.MaxIV, pkm.MaxIV, pkm.MaxIV };
LoadIVs(IVs);
}
else
LoadIVs(pkm.SetRandomIVs());
}
private void UpdateCharacteristic(int characteristic)
{
L_Characteristic.Visible = Label_CharacteristicPrefix.Visible = characteristic > -1;
if (characteristic > -1)
L_Characteristic.Text = GameInfo.Strings.characteristics[characteristic];
}
private void RecolorStatLabels(int nature)
{
// Reset Label Colors
foreach (var label in L_Stats.Skip(1))
label.ResetForeColor();
// Set Colored StatLabels only if Nature isn't Neutral
if (PKX.GetNatureModification(nature, out int incr, out int decr))
return;
L_Stats[incr].ForeColor = Color.Red;
L_Stats[decr].ForeColor = Color.Blue;
}
public string UpdateNatureModification(int nature)
{
// Reset Label Colors
foreach (var label in L_Stats.Skip(1))
label.ResetForeColor();
// Set Colored StatLabels only if Nature isn't Neutral
if (PKX.GetNatureModification(nature, out int incr, out int decr))
return "-/-";
return $"+{L_Stats[incr].Text} / -{L_Stats[decr].Text}".Replace(":", "");
}
public void SetATKIVGender(int gender)
{
pkm.SetATKIVGender(gender);
TB_ATKIV.Text = pkm.IV_ATK.ToString();
}
public void LoadPartyStats(PKM pk)
{
int size = pk.SIZE_PARTY;
if (pk.Data.Length != size)
Array.Resize(ref pk.Data, size);
Stat_HP.Text = pk.Stat_HPCurrent.ToString();
Stat_ATK.Text = pk.Stat_ATK.ToString();
Stat_DEF.Text = pk.Stat_DEF.ToString();
Stat_SPA.Text = pk.Stat_SPA.ToString();
Stat_SPD.Text = pk.Stat_SPD.ToString();
Stat_SPE.Text = pk.Stat_SPE.ToString();
}
public void SavePartyStats(PKM pk)
{
int size = pk.SIZE_PARTY;
if (pk.Data.Length != size)
Array.Resize(ref pk.Data, size);
pk.Stat_HPCurrent = Util.ToInt32(Stat_HP.Text);
pk.Stat_HPMax = Util.ToInt32(Stat_HP.Text);
pk.Stat_ATK = Util.ToInt32(Stat_ATK.Text);
pk.Stat_DEF = Util.ToInt32(Stat_DEF.Text);
pk.Stat_SPE = Util.ToInt32(Stat_SPE.Text);
pk.Stat_SPA = Util.ToInt32(Stat_SPA.Text);
pk.Stat_SPD = Util.ToInt32(Stat_SPD.Text);
}
public void LoadEVs(int[] EVs)
{
ChangingFields = true;
TB_HPEV.Text = EVs[0].ToString();
TB_ATKEV.Text = EVs[1].ToString();
TB_DEFEV.Text = EVs[2].ToString();
TB_SPEEV.Text = EVs[3].ToString();
TB_SPAEV.Text = EVs[4].ToString();
TB_SPDEV.Text = EVs[5].ToString();
ChangingFields = false;
}
public void LoadIVs(int[] IVs)
{
ChangingFields = true;
TB_HPIV.Text = IVs[0].ToString();
TB_ATKIV.Text = IVs[1].ToString();
TB_DEFIV.Text = IVs[2].ToString();
TB_SPEIV.Text = IVs[3].ToString();
TB_SPAIV.Text = IVs[4].ToString();
TB_SPDIV.Text = IVs[5].ToString();
ChangingFields = false;
LoadHyperTraining();
RefreshDerivedValues(TB_SPDIV);
UpdateStats();
}
public void ToggleInterface(int gen)
{
FLP_StatsTotal.Visible = gen >= 3;
FLP_Characteristic.Visible = gen >= 3;
CB_HPType.Enabled = gen >= 3;
FLP_HPType.Visible = gen >= 2;
switch (gen)
{
case 1:
FLP_SpD.Visible = false;
Label_SPA.Visible = false;
Label_SPC.Visible = true;
TB_HPIV.Enabled = false;
SetMaskSize(Stat_HP.Size, "00000");
break;
case 2:
FLP_SpD.Visible = true;
Label_SPA.Visible = true;
Label_SPC.Visible = false;
TB_HPIV.Enabled = false;
SetMaskSize(Stat_HP.Size, "00000");
TB_SPDEV.Enabled = TB_SPDIV.Enabled = false;
break;
default:
FLP_SpD.Visible = true;
Label_SPA.Visible = true;
Label_SPC.Visible = false;
TB_HPIV.Enabled = true;
SetMaskSize(TB_EVTotal.Size, "000");
TB_SPDEV.Enabled = TB_SPDIV.Enabled = true;
break;
}
void SetMaskSize(Size s, string Mask)
{
foreach (var ctrl in MT_EVs)
{
ctrl.Size = s;
ctrl.Mask = Mask;
}
}
}
public void InitializeDataSources()
{
CB_HPType.DisplayMember = nameof(ComboItem.Text);
CB_HPType.ValueMember = nameof(ComboItem.Value);
CB_HPType.DataSource = Util.GetCBList(GameInfo.Strings.types.Skip(1).Take(16).ToArray(), null);
}
}
public static class Extensions
{
private static readonly string[] PotentialUnicode = {"★☆☆☆", "★★☆☆", "★★★☆", "★★★★"};
private static readonly string[] PotentialNoUnicode = {"+", "++", "+++", "++++"};
public static string GetPotentialString(this PKM pkm, bool unicode = true)
{
var arr = unicode ? PotentialUnicode : PotentialNoUnicode;
return arr[pkm.PotentialRating];
}
}
}