Refactoring

WinForms->Core logic absorbing (CommonEdits)
loading ShowdownSet now applies properties to PKM instead of PKMEditor
Contest/IVs for Static/Trades are no longer set by default (less object
allocations), and are now checked by the encounter generator
This commit is contained in:
Kurt 2018-03-10 18:03:09 -08:00
parent 9da1913f1b
commit 2bd9d99d9e
9 changed files with 595 additions and 304 deletions

View File

@ -26,10 +26,10 @@ public class EncounterStatic : IEncounterable, IMoveset, IGeneration, ILocation
public bool Gift { get; set; }
public int Ball { get; set; } = 4; // Only checked when is Gift
public GameVersion Version = GameVersion.Any;
public int[] IVs { get; set; } = { -1, -1, -1, -1, -1, -1 };
public int[] IVs { get; set; }
public int FlawlessIVCount { get; internal set; }
public bool IV3 { set => FlawlessIVCount = value ? 3 : 0; }
public int[] Contest { get; set; } = { 0, 0, 0, 0, 0, 0 };
public int[] Contest { get; set; }
public int HeldItem { get; set; }
public int EggCycles { get; set; }
@ -44,8 +44,8 @@ private void CloneArrays()
// dereference original arrays with new copies
Moves = (int[])Moves?.Clone();
Relearn = (int[])Relearn.Clone();
IVs = (int[])IVs.Clone();
Contest = (int[])Contest.Clone();
IVs = (int[])IVs?.Clone();
Contest = (int[])Contest?.Clone();
}
internal virtual EncounterStatic Clone()
{

View File

@ -21,8 +21,8 @@ public class EncounterTrade : IEncounterable, IMoveset, IGeneration, ILocation
public int TID { get; set; }
public int SID { get; set; }
public GameVersion Version { get; set; } = GameVersion.Any;
public int[] IVs { get; set; } = { -1, -1, -1, -1, -1, -1 };
public int[] Contest { get; set; } = { 0, 0, 0, 0, 0, 0 };
public int[] IVs { get; set; }
public int[] Contest { get; set; }
public int Form { get; set; }
public bool Shiny { get; set; } = false;
public int Gender { get; set; } = -1;

View File

@ -513,11 +513,16 @@ private static bool GetIsMatchStatic(PKM pkm, EncounterStatic e, int lvl)
if (e.EggLocation == 60002 && e.Relearn[0] == 0 && pkm.RelearnMoves.Any(z => z != 0)) // gen7 eevee edge case
return false;
if (e.Generation > 2 || pkm.Format <= 2) // 1,2->7 regenerates IVs, only check if original IVs still exist
if (e.IVs != null && (e.Generation > 2 || pkm.Format <= 2)) // 1,2->7 regenerates IVs, only check if original IVs still exist
for (int i = 0; i < 6; i++)
if (e.IVs[i] != -1 && e.IVs[i] != pkm.IVs[i])
return false;
if (e.Contest != null)
for (int i = 0; i < 6; i++)
if (e.Contest[i] > pkm.Contest[i])
return false;
// Defer to EC/PID check
// if (e.Shiny != null && e.Shiny != pkm.IsShiny)
// continue;
@ -907,7 +912,7 @@ private static IEnumerable<EncounterTrade> GetValidEncounterTradesVC2(PKM pkm, D
continue;
if (z.Gender >= 0 && z.Gender != pkm.Gender && pkm.Format <= 2)
continue;
if (z.IVs[0] >= 0 && !z.IVs.SequenceEqual(pkm.IVs) && pkm.Format <= 2)
if (z.IVs != null && !z.IVs.SequenceEqual(pkm.IVs) && pkm.Format <= 2)
continue;
if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126)
continue;
@ -995,6 +1000,7 @@ private static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, Game
}
private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl)
{
if (z.IVs != null)
for (int i = 0; i < 6; i++)
if (z.IVs[i] != -1 && z.IVs[i] != pkm.IVs[i])
return false;
@ -1047,6 +1053,11 @@ private static bool IsEncounterTradeValid(PKM pkm, EncounterTrade z, int lvl)
// if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability
// countinue;
if (z.Contest != null)
for (int i = 0; i < 6; i++)
if (z.Contest[i] > pkm.Contest[i])
return false;
return true;
}

View File

@ -0,0 +1,432 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
/// <summary>
/// Contains extension logic for modifying <see cref="PKM"/> data.
/// </summary>
public static class CommonEdits
{
/// <summary>
/// Sets the <see cref="PKM.Nickname"/> to the provided value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nick"><see cref="PKM.Nickname"/> to set. If no nickname is provided, the <see cref="PKM.Nickname"/> is set to the default value for its current language and format.</param>
public static void SetNickname(this PKM pk, string nick = null)
{
if (nick != null)
{
pk.IsNicknamed = true;
pk.Nickname = nick;
}
else
{
pk.IsNicknamed = false;
pk.Nickname = PKX.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format);
if (pk is PK1 pk1) pk1.SetNotNicknamed();
if (pk is PK2 pk2) pk2.SetNotNicknamed();
}
}
/// <summary>
/// Sets the <see cref="PKM.AltForm"/> value, with special consideration for <see cref="PKM.Format"/> values which derive the <see cref="PKM.AltForm"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="form">Desired <see cref="PKM.AltForm"/> value to set.</param>
public static void SetAltForm(this PKM pk, int form)
{
switch (pk.Format)
{
case 2:
while (pk.AltForm != form)
pk.SetRandomIVs();
break;
case 3:
pk.SetPIDUnown3(form);
break;
default:
pk.AltForm = form;
break;
}
}
/// <summary>
/// Sets the <see cref="PKM.Ability"/> value by sanity checking the provided <see cref="PKM.Ability"/> against the possible pool of abilities.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="abil">Desired <see cref="PKM.Ability"/> to set.</param>
public static void SetAbility(this PKM pk, int abil)
{
var abilities = pk.PersonalInfo.Abilities;
int abilIndex = Array.IndexOf(abilities, abil);
abilIndex = Math.Max(0, abilIndex);
if (pk is PK5 pk5 && abilIndex == 2)
pk5.HiddenAbility = true;
else if (pk.Format <= 5)
pk.PID = PKX.GetRandomPID(pk.Species, pk.Gender, pk.Version, pk.Nature, pk.Format, (uint)(abilIndex * 0x10001));
pk.RefreshAbility(abilIndex);
}
/// <summary>
/// Sets a Random <see cref="PKM.EncryptionConstant"/> value. The <see cref="PKM.EncryptionConstant"/> is not updated if the value should match the <see cref="PKM.PID"/> instead.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetRandomEC(this PKM pk)
{
int gen = pk.GenNumber;
if (gen < 6 && gen > 2)
pk.EncryptionConstant = pk.PID;
else
pk.EncryptionConstant = Util.Rand32();
}
/// <summary>
/// Sets the <see cref="PKM.IsShiny"/> derived value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="shiny">Desired <see cref="PKM.IsShiny"/> state to set.</param>
/// <returns></returns>
public static bool SetIsShiny(this PKM pk, bool shiny) => shiny ? pk.SetShiny() : pk.SetUnshiny();
/// <summary>
/// Makes a <see cref="PKM"/> shiny.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <returns>Returns <see cref="bool.True"/> if the <see cref="PKM"/> data was modified.</returns>
public static bool SetShiny(this PKM pk)
{
if (pk.IsShiny)
return false;
if (pk.Format > 2)
pk.SetShinyPID();
else
pk.SetShinyIVs();
return true;
}
/// <summary>
/// Makes a <see cref="PKM"/> not-shiny.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <returns>Returns <see cref="bool.True"/> if the <see cref="PKM"/> data was modified.</returns>
public static bool SetUnshiny(this PKM pk)
{
if (!pk.IsShiny)
return false;
pk.SetPIDGender(pk.Gender);
return true;
}
/// <summary>
/// Sets the <see cref="PKM.Nature"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Nature"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public static void SetNature(this PKM pk, int nature)
{
if (pk.Format <= 4)
pk.SetPIDNature(Math.Max(0, nature));
else
pk.Nature = Math.Max(0, nature);
}
/// <summary>
/// Sets the <see cref="PKM.Nature"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Nature"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public static void SetNature(this PKM pk, Nature nature) => pk.SetNature((int) nature);
/// <summary>
/// Sets the individual PP Up count values depending if a Move is present in the moveslot or not.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Moves"><see cref="PKM.Moves"/> to use (if already known). Will fetch the current <see cref="PKM.Moves"/> if not provided.</param>
public static void SetPPUps(this PKM pk, int[] Moves = null)
{
if (Moves == null)
Moves = pk.Moves;
pk.Move1_PP = pk.GetMovePP(Moves[0], pk.Move1_PPUps = GetPPUpCount(Moves[0]));
pk.Move2_PP = pk.GetMovePP(Moves[1], pk.Move2_PPUps = GetPPUpCount(Moves[1]));
pk.Move3_PP = pk.GetMovePP(Moves[2], pk.Move3_PPUps = GetPPUpCount(Moves[2]));
pk.Move4_PP = pk.GetMovePP(Moves[3], pk.Move4_PPUps = GetPPUpCount(Moves[3]));
int GetPPUpCount(int moveID) => moveID > 0 ? 3 : 0;
}
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
public static void SetGender(this PKM pk, string gender)
{
if (gender == null)
return;
int Gender = PKX.GetGenderFromString(gender);
pk.SetGender(Gender);
}
/// <summary>
/// Sets the <see cref="PKM.Gender"/> value, with special consideration for the <see cref="PKM.Format"/> values which derive the <see cref="PKM.Gender"/> value.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/> value to set.</param>
public static void SetGender(this PKM pk, int gender)
{
gender = Math.Min(2, Math.Max(0, gender));
if (pk.Format <= 2)
pk.SetATKIVGender(gender);
else if (pk.Format <= 4)
pk.SetPIDGender(gender);
else
pk.Gender = gender;
}
/// <summary>
/// Sets <see cref="PKM.HyperTrainFlags"/> to valid values which may best enhance the <see cref="PKM"/> stats.
/// </summary>
/// <param name="pkm"></param>
/// <param name="IVs"><see cref="PKM.IVs"/> to use (if already known). Will fetch the current <see cref="PKM.IVs"/> if not provided.</param>
public static void SetSuggestedHyperTrainingData(this PKM pkm, int[] IVs = null)
{
if (pkm.Format < 7)
return;
if (pkm.CurrentLevel < 100)
{
pkm.HyperTrainFlags = 0;
return;
}
if (IVs == null)
IVs = pkm.IVs;
pkm.HT_HP = IVs[0] != 31;
pkm.HT_ATK = IVs[1] != 31 && IVs[1] > 2;
pkm.HT_DEF = IVs[2] != 31;
pkm.HT_SPE = IVs[3] != 31 && IVs[3] > 2;
pkm.HT_SPA = IVs[4] != 31;
pkm.HT_SPD = IVs[5] != 31;
}
/// <summary>
/// Fetches <see cref="PKM.RelearnMoves"/> based on the provided <see cref="LegalityAnalysis"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="legal"><see cref="LegalityAnalysis"/> which contains parsed information pertaining to legality.</param>
/// <returns><see cref="PKM.RelearnMoves"/> best suited for the current <see cref="PKM"/> data.</returns>
public static int[] GetSuggestedRelearnMoves(this PKM pk, LegalityAnalysis legal)
{
int[] m = legal.GetSuggestedRelearn();
if (!m.All(z => z == 0))
return m;
if (pk.WasEgg || pk.WasEvent || pk.WasEventEgg || pk.WasLink)
return m;
var encounter = legal.GetSuggestedMetInfo();
if (encounter != null)
m = encounter.Relearn;
return m;
}
/// <summary>
/// Sanity checks the provided <see cref="PKM.Gender"/> value, and returns a sane value.
/// </summary>
/// <param name="pkm"></param>
/// <param name="cg">Current <see cref="PKM.Gender"/> preference</param>
/// <returns>Most-legal <see cref="PKM.Gender"/> value</returns>
public static int GetSaneGender(this PKM pkm, int cg)
{
int gt = pkm.PersonalInfo.Gender;
switch (gt)
{
case 255: return 2; // Genderless
case 254: return 1; // Female-Only
case 0: return 0; // Male-Only
}
if (cg == 2 || pkm.GenNumber < 6)
return (byte)pkm.PID <= gt ? 1 : 0;
return cg;
}
/// <summary>
/// Copies <see cref="ShowdownSet"/> details to the <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="Set"><see cref="ShowdownSet"/> details to copy from.</param>
public static void ApplySetDetails(this PKM pk, ShowdownSet Set)
{
pk.Species = Set.Species;
pk.Moves = Set.Moves;
pk.HeldItem = Set.HeldItem < 0 ? 0 : Set.HeldItem;
pk.CurrentLevel = Set.Level;
pk.CurrentFriendship = Set.Friendship;
pk.IVs = Set.IVs;
pk.EVs = Set.EVs;
pk.ApplyMarkings();
pk.SetSuggestedHyperTrainingData(Set.IVs);
pk.SetNickname(Set.Nickname);
pk.SetGender(Set.Gender);
pk.SetAltForm(Set.FormIndex);
pk.SetPPUps(Set.Moves);
pk.SetAbility(Set.Ability);
pk.SetNature(Set.Nature);
pk.SetIsShiny(Set.Shiny);
pk.SetRandomEC();
var legal = new LegalityAnalysis(pk);
if (legal.Info.Relearn.Any(z => !z.Valid))
pk.RelearnMoves = pk.GetSuggestedRelearnMoves(legal);
}
/// <summary>
/// Sets all Memory related data to the default value (zero).
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void ClearMemories(this PKM pk)
{
pk.OT_Memory = pk.OT_Affection = pk.OT_Feeling = pk.OT_Intensity = pk.OT_TextVar =
pk.HT_Memory = pk.HT_Affection = pk.HT_Feeling = pk.HT_Intensity = pk.HT_TextVar = 0;
}
/// <summary>
/// Sets the <see cref="PKM.Markings"/> to indicate flawless (or near-flawless) <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="IVs"><see cref="PKM.IVs"/> to use (if already known). Will fetch the current <see cref="PKM.IVs"/> if not provided.</param>
public static void ApplyMarkings(this PKM pk, int[] IVs = null)
{
if (pk.Format <= 3)
return; // no markings (gen3 only has 4; can't mark stats intelligently
if (IVs == null)
IVs = pk.IVs;
var remapper = pk.Format < 7 ? (Func<int, int>)GetSimpleMarking : GetComplexMarking;
pk.Markings = IVs.Select(remapper).ToArray();
int GetSimpleMarking(int val) => val == 31 ? 1 : 0;
int GetComplexMarking(int val)
{
if (val == 31 || val == 1)
return 1;
if (val == 30 || val == 0)
return 2;
return 0;
}
}
/// <summary>
/// Sets one of the <see cref="PKM.EVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public static void SetEV(this PKM pk, int index, int value)
{
switch (index)
{
case 0: pk.EV_HP = value; break;
case 1: pk.EV_ATK = value; break;
case 2: pk.EV_DEF = value; break;
case 3: pk.EV_SPE = value; break;
case 4: pk.EV_SPA = value; break;
case 5: pk.EV_SPD = value; break;
default:
throw new ArgumentOutOfRangeException(nameof(index));
}
}
/// <summary>
/// Sets one of the <see cref="PKM.IVs"/> based on its index within the array.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to set to</param>
/// <param name="value">Value to set</param>
public static void SetIV(this PKM pk, int index, int value)
{
switch (index)
{
case 0: pk.IV_HP = value; break;
case 1: pk.IV_ATK = value; break;
case 2: pk.IV_DEF = value; break;
case 3: pk.IV_SPE = value; break;
case 4: pk.IV_SPA = value; break;
case 5: pk.IV_SPD = value; break;
default:
throw new ArgumentOutOfRangeException(nameof(index));
}
}
/// <summary>
/// Updates the <see cref="PKM.IV_ATK"/> for a Generation 1/2 format <see cref="PKM"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="gender">Desired <see cref="PKM.Gender"/>.</param>
public static void SetATKIVGender(this PKM pk, int gender)
{
do { pk.IV_ATK = (int)(Util.Rand32() & pk.MaxIV); }
while (pk.Gender != gender);
}
/// <summary>
/// Fetches the highest value the provided <see cref="PKM.EVs"/> index can be while considering others.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to fetch for</param>
/// <returns>Highest value the value can be.</returns>
public static int GetMaximumEV(this PKM pk, int index)
{
if (pk.Format < 3)
return ushort.MaxValue;
var EVs = pk.EVs;
EVs[index] = 0;
var sum = EVs.Sum();
int remaining = 510 - sum;
var newEV = Math.Min(Math.Max(remaining, 0), 252);
return newEV;
}
/// <summary>
/// Fetches the highest value the provided <see cref="PKM.IVs"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="index">Index to fetch for</param>
/// <param name="Allow30">Causes the returned value to be dropped down -1 if the value is already at a maxmimum.</param>
/// <returns>Highest value the value can be.</returns>
public static int GetMaximumIV(this PKM pk, int index, bool Allow30 = false)
{
if (pk.IVs[index] == pk.MaxIV && Allow30)
return pk.MaxIV - 1;
return pk.MaxIV;
}
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hptype"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="hptype">Desired Hidden Power typing.</param>
public static void SetHiddenPower(this PKM pk, int hptype)
{
var IVs = pk.IVs;
HiddenPower.SetIVsForType(hptype, pk.IVs);
pk.IVs = IVs;
}
/// <summary>
/// Sets the <see cref="PKM.IVs"/> to match a provided <see cref="hptype"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="hptype">Desired Hidden Power typing.</param>
public static void SetHiddenPower(this PKM pk, MoveType hptype) => pk.SetHiddenPower((int) hptype);
}
}

View File

@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
public static class HiddenPower
{
public static int GetType(IReadOnlyList<int> IVs)
{
int hp = 0;
for (int i = 0; i < 6; i++)
hp |= (IVs[i] & 1) << i;
hp *= 0xF;
hp /= 0x3F;
return hp;
}
public static bool SetIVsForType(int hpVal, int[] IVs)
{
if (IVs.All(z => z == 31))
{
PKX.SetHPIVs(hpVal, IVs); // Get IVs
return true;
}
int current = GetType(IVs);
if (current == hpVal)
return true; // no mods necessary
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
int[] best = GetSuggestedHiddenPowerIVs(hpVal, IVs);
if (best == null)
return false; // can't force hidden power?
// set IVs back to array
for (int i = 0; i < IVs.Length; i++)
IVs[i] = best[i];
return true;
}
private static int[] GetSuggestedHiddenPowerIVs(int hpVal, int[] IVs)
{
var flawless = IVs.Select((v, i) => v == 31 ? i : -1).Where(v => v != -1).ToArray();
var permutations = GetPermutations(flawless, flawless.Length);
int flawedCount = 0;
int[] best = null;
foreach (var permute in permutations)
{
var ivs = (int[])IVs.Clone();
foreach (var item in permute)
{
ivs[item] ^= 1;
if (hpVal != GetType(ivs))
continue;
int ct = ivs.Count(z => z == 31);
if (ct <= flawedCount)
break; // any further flaws are always worse
flawedCount = ct;
best = ivs;
break; // any further flaws are always worse
}
}
return best;
}
private static IEnumerable<IEnumerable<T>> GetPermutations<T>(IList<T> list, int length)
{
// https://stackoverflow.com/a/10630026
if (length == 1)
return list.Select(t => new[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new[] { t2 }));
}
}
}

View File

@ -400,6 +400,16 @@ public int[] RelearnMoves
if (value.Length > 3) RelearnMove4 = value[3];
}
}
public int[] Contest
{
get => new[] { CNT_Cool, CNT_Beauty, CNT_Cute, CNT_Smart, CNT_Tough, CNT_Sheen };
set
{
if (value?.Length != 6) return;
CNT_Cool = value[0]; CNT_Beauty = value[1]; CNT_Cute = value[2];
CNT_Smart = value[3]; CNT_Tough = value[4]; CNT_Sheen = value[5];
}
}
public int PIDAbility
{
get

View File

@ -217,10 +217,7 @@ private IEnumerable<string> GetStringMoves()
{
var str = $"- {moves[move]}";
if (move == 237) // Hidden Power
{
int hp = GetHiddenPowerType(IVs);
str += $" [{hptypes[hp]}]";
}
str += $" [{hptypes[HiddenPower.GetType(IVs)]}]";
yield return str;
}
}
@ -354,7 +351,7 @@ private string ParseLineMove(string line)
if (IVs.Any(z => z != 31))
{
if (!SetIVsForHiddenPower(hpVal, IVs))
if (!HiddenPower.SetIVsForType(hpVal, IVs))
InvalidLines.Add($"Invalid IVs for Hidden Power Type: {type}");
}
else if (hpVal >= 0)
@ -365,73 +362,6 @@ private string ParseLineMove(string line)
moveString = "Hidden Power";
return moveString;
}
private static int GetHiddenPowerType(IReadOnlyList<int> IVs)
{
int hp = 0;
for (int i = 0; i < 6; i++)
hp |= (IVs[i] & 1) << i;
hp *= 0xF;
hp /= 0x3F;
return hp;
}
private static bool SetIVsForHiddenPower(int hpVal, int[] IVs)
{
if (IVs.All(z => z == 31))
{
PKX.SetHPIVs(hpVal, IVs); // Get IVs
return true;
}
int current = GetHiddenPowerType(IVs);
if (current == hpVal)
return true; // no mods necessary
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
int[] best = GetSuggestedHiddenPowerIVs(hpVal, IVs);
if (best == null)
return false; // can't force hidden power?
// set IVs back to array
for (int i = 0; i < IVs.Length; i++)
IVs[i] = best[i];
return true;
}
private static int[] GetSuggestedHiddenPowerIVs(int hpVal, int[] IVs)
{
var flawless = IVs.Select((v, i) => v == 31 ? i : -1).Where(v => v != -1).ToArray();
var permutations = GetPermutations(flawless, flawless.Length);
int flawedCount = 0;
int[] best = null;
foreach (var permute in permutations)
{
var ivs = (int[])IVs.Clone();
foreach (var item in permute)
{
ivs[item] ^= 1;
if (hpVal != GetHiddenPowerType(ivs))
continue;
int ct = ivs.Count(z => z == 31);
if (ct <= flawedCount)
break; // any further flaws are always worse
flawedCount = ct;
best = ivs;
break; // any further flaws are always worse
}
}
return best;
}
private static IEnumerable<IEnumerable<T>> GetPermutations<T>(IList<T> list, int length)
{
// https://stackoverflow.com/a/10630026
if (length == 1)
return list.Select(t => new[] { t });
return GetPermutations(list, length - 1)
.SelectMany(t => list.Where(e => !t.Contains(e)),
(t1, t2) => t1.Concat(new[] { t2 }));
}
private void ParseLineEVs(string line)
{
string[] evlist = SplitLineStats(line);
@ -563,41 +493,5 @@ private static string ReplaceAll(string original, string to, params string[] toB
{
return toBeReplaced.Aggregate(original, (current, v) => current.Replace(v, to));
}
public void ApplyToPKM(PKM pkm)
{
if (Species < 0)
return;
pkm.Species = Species;
if (Nickname != null && Nickname.Length <= pkm.NickLength)
pkm.Nickname = Nickname;
else
pkm.Nickname = PKX.GetSpeciesName(pkm.Species, pkm.Language);
int gender = PKX.GetGenderFromString(Gender);
pkm.Gender = Math.Max(0, gender);
var list = PKX.GetFormList(pkm.Species, types, forms, genders);
int formnum = Array.IndexOf(list, Form);
pkm.AltForm = Math.Max(0, formnum);
var abils = pkm.PersonalInfo.Abilities;
int index = Array.IndexOf(abils, Ability);
pkm.RefreshAbility(Math.Max(0, Math.Min(2, index)));
if (Shiny && !pkm.IsShiny)
pkm.SetShinyPID();
else if (!Shiny && pkm.IsShiny)
pkm.PID = Util.Rand32();
pkm.CurrentLevel = Level;
pkm.HeldItem = Math.Max(0, HeldItem);
pkm.CurrentFriendship = Friendship;
pkm.Nature = Nature;
pkm.EVs = EVs;
pkm.IVs = IVs;
pkm.Moves = Moves;
}
}
}

View File

@ -26,6 +26,16 @@ public PKMEditor()
TB_OT.Font = (Font)TB_Nickname.Font.Clone();
TB_OTt2.Font = (Font)TB_Nickname.Font.Clone();
// Commonly reused Control arrays
Moves = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
Relearn = new[] { CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 };
PPUps = new[] { CB_PPu1, CB_PPu2, CB_PPu3, CB_PPu4 };
MovePP = new[] { TB_PP1, TB_PP2, TB_PP3, TB_PP4 };
ValidationRequired = Moves.Concat(Relearn).Concat(new[]
{
CB_Species, CB_Nature, CB_HeldItem, CB_Ability, // Main Tab
CB_MetLocation, CB_EggLocation, CB_Ball, // Met Tab
}).ToArray();
relearnPB = new[] { PB_WarnRelearn1, PB_WarnRelearn2, PB_WarnRelearn3, PB_WarnRelearn4 };
movePB = new[] { PB_WarnMove1, PB_WarnMove2, PB_WarnMove3, PB_WarnMove4 };
foreach (var c in WinFormsUtil.GetAllControlsOfType(this, typeof(ComboBox)))
@ -33,6 +43,7 @@ public PKMEditor()
Stats.SetMainEditor(this);
LoadShowdownSet = LoadShowdownSetDefault;
}
private void UpdateStats() => Stats.UpdateStats();
@ -75,6 +86,9 @@ private void SavePartyStats(PKM pk)
public bool PKMIsUnsaved => FieldsInitialized && FieldsLoaded && LastData != null && LastData.Any(b => b != 0) && !LastData.SequenceEqual(CurrentPKM.Data);
public bool IsEmptyOrEgg => CHK_IsEgg.Checked || CB_Species.SelectedIndex == 0;
private readonly ComboBox[] Moves, Relearn, ValidationRequired, PPUps;
private readonly MaskedTextBox[] MovePP;
private bool forceValidation;
public PKM PreparePKM(bool click = true)
{
@ -92,14 +106,8 @@ public bool VerifiedPKM()
if (ModifierKeys == (Keys.Control | Keys.Shift | Keys.Alt))
return true; // Override
// Make sure the PKX Fields are filled out properly (color check)
ComboBox[] cba = {
CB_Species, CB_Nature, CB_HeldItem, CB_Ability, // Main Tab
CB_MetLocation, CB_EggLocation, CB_Ball, // Met Tab
CB_Move1, CB_Move2, CB_Move3, CB_Move4, // Moves
CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 // Moves
};
ComboBox cb = cba.FirstOrDefault(c => c.BackColor == Color.DarkSalmon && c.Items.Count != 0);
var cb = ValidationRequired.FirstOrDefault(c => c.BackColor == Color.DarkSalmon && c.Items.Count != 0);
if (cb != null)
{
Control c = cb.Parent; while (!(c is TabPage)) c = c.Parent;
@ -232,6 +240,7 @@ public void UpdateLegality(LegalityAnalysis la = null, bool skipMoveRepop = fals
{
PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible =
PB_WarnRelearn1.Visible = PB_WarnRelearn2.Visible = PB_WarnRelearn3.Visible = PB_WarnRelearn4.Visible = false;
LegalityChanged?.Invoke(Legality.Valid, null);
return;
}
@ -248,10 +257,9 @@ public void UpdateLegality(LegalityAnalysis la = null, bool skipMoveRepop = fals
// Resort moves
bool tmp = FieldsLoaded;
FieldsLoaded = false;
var cb = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
var moves = Legality.AllSuggestedMovesAndRelearn;
var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList();
foreach (ComboBox c in cb)
foreach (var c in Moves)
{
var index = WinFormsUtil.GetIndex(c);
c.DataSource = new BindingSource(moveList, null);
@ -465,8 +473,7 @@ private static bool GetMarkingColor(int markval, out Color c)
private void UpdateGender()
{
int cg = PKX.GetGenderFromString(Label_Gender.Text);
int Gender = GetSaneGender(pkm, cg);
int Gender = pkm.GetSaneGender(cg);
Label_Gender.Text = gendersymbols[Gender];
Label_Gender.ForeColor = GetGenderColor(Gender);
}
@ -660,25 +667,16 @@ private bool SetSuggestedMoves(bool random = false, bool silent = false)
return false;
}
CB_Move1.SelectedValue = m[0];
CB_Move2.SelectedValue = m[1];
CB_Move3.SelectedValue = m[2];
CB_Move4.SelectedValue = m[3];
pkm.Moves = m;
pkm.SetPPUps(m);
LoadMoves(pkm);
return true;
}
private bool SetSuggestedRelearnMoves(bool silent = false)
{
if (pkm.Format < 6)
return false;
int[] m = Legality.GetSuggestedRelearn();
if (m.All(z => z == 0))
if (!pkm.WasEgg && !pkm.WasEvent && !pkm.WasEventEgg && !pkm.WasLink)
{
var encounter = Legality.GetSuggestedMetInfo();
if (encounter != null)
m = encounter.Relearn;
}
int[] m = pkm.GetSuggestedRelearnMoves(Legality);
if (pkm.RelearnMoves.SequenceEqual(m))
return false;
@ -720,17 +718,7 @@ private bool SetSuggestedMetLocation(bool silent = false)
if (!silent)
{
var suggestion = new List<string> { "Suggested:" };
if (pkm.Format >= 3)
{
var met_list = GameInfo.GetLocationList((GameVersion)pkm.Version, pkm.Format, egg: false);
var locstr = met_list.FirstOrDefault(loc => loc.Value == location).Text;
suggestion.Add($"Met Location: {locstr}");
suggestion.Add($"Met Level: {level}");
}
if (pkm.CurrentLevel < minlvl)
suggestion.Add($"Current Level: {minlvl}");
List<string> suggestion = GetSuggestionMessage(pkm, level, location, minlvl);
if (suggestion.Count == 1) // no suggestion
return false;
@ -910,23 +898,21 @@ private void UpdateHaXForm(object sender, EventArgs e)
}
private void UpdatePP(object sender, EventArgs e)
{
ComboBox[] cbs = { CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
ComboBox[] pps = { CB_PPu1, CB_PPu2, CB_PPu3, CB_PPu4 };
MaskedTextBox[] tbs = { TB_PP1, TB_PP2, TB_PP3, TB_PP4 };
int index = Array.IndexOf(cbs, sender);
if (!(sender is ComboBox cb))
return;
int index = Array.IndexOf(Moves, cb);
if (index < 0)
index = Array.IndexOf(pps, sender);
index = Array.IndexOf(PPUps, cb);
if (index < 0)
return;
int move = WinFormsUtil.GetIndex(cbs[index]);
int pp = pps[index].SelectedIndex;
if (move == 0 && pp != 0)
{
pps[index].SelectedIndex = 0;
return; // recursively triggers
}
tbs[index].Text = pkm.GetMovePP(move, pp).ToString();
int move = WinFormsUtil.GetIndex(Moves[index]);
var ppctrl = PPUps[index];
int ppups = ppctrl.SelectedIndex;
if (move <= 0)
ppctrl.SelectedIndex = 0;
else
MovePP[index].Text = pkm.GetMovePP(move, ppups).ToString();
}
private void UpdatePKRSstrain(object sender, EventArgs e)
{
@ -1104,13 +1090,13 @@ private void UpdateOriginGame(object sender, EventArgs e)
}
private void UpdateExtraByteValue(object sender, EventArgs e)
{
if (CB_ExtraBytes.Items.Count == 0)
if (CB_ExtraBytes.Items.Count == 0 || !(sender is MaskedTextBox mtb))
return;
// Changed Extra Byte's Value
if (Util.ToInt32(((MaskedTextBox)sender).Text) > byte.MaxValue)
((MaskedTextBox)sender).Text = "255";
if (Util.ToInt32(mtb.Text) > byte.MaxValue)
mtb.Text = "255";
int value = Util.ToInt32(TB_ExtraByte.Text);
int value = Util.ToInt32(mtb.Text);
int offset = Convert.ToInt32(CB_ExtraBytes.Text, 16);
pkm.Data[offset] = (byte)value;
}
@ -1269,8 +1255,7 @@ private void UpdateIsEgg(object sender, EventArgs e)
// Wipe egg memories
if (pkm.Format >= 6 && ModifyPKM)
pkm.OT_Memory = pkm.OT_Affection = pkm.OT_Feeling = pkm.OT_Intensity = pkm.OT_TextVar =
pkm.HT_Memory = pkm.HT_Affection = pkm.HT_Feeling = pkm.HT_Intensity = pkm.HT_TextVar = 0;
pkm.ClearMemories();
}
else // Not Egg
{
@ -1467,12 +1452,12 @@ private void ValidateMove(object sender, EventArgs e)
if (!FieldsLoaded)
return;
if (new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }.Contains(sender)) // Move
if (Moves.Contains(sender)) // Move
UpdatePP(sender, e);
// Legality
pkm.Moves = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 }.Select(WinFormsUtil.GetIndex).ToArray();
pkm.RelearnMoves = new[] { CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 }.Select(WinFormsUtil.GetIndex).ToArray();
pkm.Moves = Moves.Select(WinFormsUtil.GetIndex).ToArray();
pkm.RelearnMoves = Relearn.Select(WinFormsUtil.GetIndex).ToArray();
UpdateLegality(skipMoveRepop: true);
}
private void ValidateMovePaint(object sender, DrawItemEventArgs e)
@ -1673,56 +1658,9 @@ public void EnableDragDrop(DragEventHandler enter, DragEventHandler drop)
public Action<ShowdownSet> LoadShowdownSet;
private void LoadShowdownSetDefault(ShowdownSet Set)
{
CB_Species.SelectedValue = Set.Species;
CHK_Nicknamed.Checked = Set.Nickname != null;
if (Set.Nickname != null)
TB_Nickname.Text = Set.Nickname;
if (Set.Gender != null)
{
int Gender = PKX.GetGenderFromString(Set.Gender);
Label_Gender.Text = gendersymbols[Gender];
Label_Gender.ForeColor = GetGenderColor(Gender);
}
CB_Form.SelectedIndex = Math.Min(CB_Form.Items.Count - 1, Set.FormIndex);
// Set Ability and Moves
int abil = Math.Max(0, Array.IndexOf(pkm.PersonalInfo.Abilities, Set.Ability));
CB_Ability.SelectedIndex = Math.Min(CB_Ability.Items.Count - 1, abil);
ComboBox[] m = { CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
ComboBox[] p = { CB_PPu1, CB_PPu2, CB_PPu3, CB_PPu4 };
for (int i = 0; i < 4; i++)
{
m[i].SelectedValue = Set.Moves[i];
p[i].SelectedIndex = Set.Moves[i] != 0 ? 3 : 0; // max PP
}
// Set Item and Nature
CB_HeldItem.SelectedValue = Set.HeldItem < 0 ? 0 : Set.HeldItem;
CB_Nature.SelectedValue = Set.Nature < 0 ? 0 : Set.Nature;
// Set Level and Friendship
TB_Level.Text = Set.Level.ToString();
TB_Friendship.Text = Set.Friendship.ToString();
SetSuggestedHyperTrainingData(pkm, Set.IVs);
// Set IVs
Stats.LoadIVs(Set.IVs);
Stats.LoadEVs(Set.EVs);
UpdateRandomEC(null, null);
if (Set.Shiny && !pkm.IsShiny)
UpdateShiny(true);
else if (!Set.Shiny && pkm.IsShiny)
UpdateRandomPID(BTN_RerollPID, null);
else
UpdateRandomPID(null, null);
pkm = PreparePKM();
UpdateLegality();
if (Legality.Info.Relearn.Any(z => !z.Valid))
SetSuggestedRelearnMoves(silent: true);
var pk = PreparePKM();
pk.ApplySetDetails(Set);
PopulateFields(pk);
}
public void ChangeLanguage(SaveFile sav, PKM pk)
@ -1786,42 +1724,26 @@ private void PopulateFilteredDataSources(SaveFile SAV)
// Set the Move ComboBoxes too..
GameInfo.MoveDataSource = (HaX ? GameInfo.HaXMoveDataSource : GameInfo.LegalMoveDataSource).Where(m => m.Value <= SAV.MaxMoveID).ToList(); // Filter Z-Moves if appropriate
foreach (ComboBox cb in new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4, CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 })
foreach (var cb in Moves.Concat(Relearn))
{
cb.DisplayMember = "Text"; cb.ValueMember = "Value";
cb.DataSource = new BindingSource(GameInfo.MoveDataSource, null);
}
}
private static int GetSaneGender(PKM pkm, int cg)
private static List<string> GetSuggestionMessage(PKM pkm, int level, int location, int minlvl)
{
int gt = pkm.PersonalInfo.Gender;
if (gt == 255) // Genderless
return 2;
if (gt == 254) // Female Only
return 1;
if (gt == 0) // Male Only
return 0;
if (cg == 2 || pkm.GenNumber < 6)
return (byte)pkm.PID <= gt ? 1 : 0;
return cg;
}
private static void SetSuggestedHyperTrainingData(PKM pkm, int[] IVs)
{
if (pkm.Format < 7)
return;
if (pkm.CurrentLevel < 100)
var suggestion = new List<string> { "Suggested:" };
if (pkm.Format >= 3)
{
pkm.HyperTrainFlags = 0;
return;
var met_list = GameInfo.GetLocationList((GameVersion)pkm.Version, pkm.Format, egg: false);
var locstr = met_list.FirstOrDefault(loc => loc.Value == location).Text;
suggestion.Add($"Met Location: {locstr}");
suggestion.Add($"Met Level: {level}");
}
pkm.HT_HP = IVs[0] != 31;
pkm.HT_ATK = IVs[1] != 31 && IVs[1] > 2;
pkm.HT_DEF = IVs[2] != 31;
pkm.HT_SPE = IVs[3] != 31 && IVs[3] > 2;
pkm.HT_SPA = IVs[4] != 31;
pkm.HT_SPD = IVs[5] != 31;
if (pkm.CurrentLevel < minlvl)
suggestion.Add($"Current Level: {minlvl}");
return suggestion;
}
}
}

View File

@ -397,59 +397,5 @@ public static string GetPotentialString(this PKM pkm, bool unicode = true)
var arr = unicode ? PotentialUnicode : PotentialNoUnicode;
return arr[pkm.PotentialRating];
}
public static void SetEV(this PKM pk, int index, int value)
{
switch (index)
{
case 0: pk.EV_HP = value; break;
case 1: pk.EV_ATK = value; break;
case 2: pk.EV_DEF = value; break;
case 3: pk.EV_SPE = value; break;
case 4: pk.EV_SPA = value; break;
case 5: pk.EV_SPD = value; break;
default:
throw new ArgumentOutOfRangeException(nameof(index));
}
}
public static void SetIV(this PKM pk, int index, int value)
{
switch (index)
{
case 0: pk.IV_HP = value; break;
case 1: pk.IV_ATK = value; break;
case 2: pk.IV_DEF = value; break;
case 3: pk.IV_SPE = value; break;
case 4: pk.IV_SPA = value; break;
case 5: pk.IV_SPD = value; break;
default:
throw new ArgumentOutOfRangeException(nameof(index));
}
}
public static void SetATKIVGender(this PKM pk, int gender)
{
do { pk.IV_ATK = (int)(Util.Rand32() & pk.MaxIV); }
while (pk.Gender != gender);
}
public static int GetMaximumEV(this PKM pkm, int index)
{
if (pkm.Format < 3)
return ushort.MaxValue;
var EVs = pkm.EVs;
EVs[index] = 0;
var sum = EVs.Sum();
int remaining = 510 - sum;
var newEV = Math.Min(Math.Max(remaining, 0), 252);
return newEV;
}
public static int GetMaximumIV(this PKM pkm, int index, bool Allow30 = false)
{
if (pkm.IVs[index] == pkm.MaxIV && Allow30)
return pkm.MaxIV - 1;
return pkm.MaxIV;
}
}
}