using System;
using System.Linq;
namespace PKHeX.Core
{
///
/// Contains extension logic for modifying data.
///
public static class CommonEdits
{
///
/// Setting which enables/disables automatic manipulation of when importing from a .
///
public static bool ShowdownSetIVMarkings { get; set; } = true;
///
/// Setting which causes the to the in Gen8+ formats.
///
public static bool ShowdownSetBehaviorNature { get; set; }
///
/// Sets the to the provided value.
///
/// Pokémon to modify.
/// to set. If no nickname is provided, the is set to the default value for its current language and format.
public static void SetNickname(this PKM pk, string nick)
{
if (string.IsNullOrWhiteSpace(nick))
{
pk.ClearNickname();
return;
}
pk.IsNicknamed = true;
pk.Nickname = nick;
}
///
/// Clears the to the default value.
///
///
public static string ClearNickname(this PKM pk)
{
pk.IsNicknamed = false;
string nick = SpeciesName.GetSpeciesNameGeneration(pk.Species, pk.Language, pk.Format);
pk.Nickname = nick;
if (pk is GBPKM pk12)
pk12.SetNotNicknamed();
return nick;
}
///
/// Sets the value, with special consideration for values which derive the value.
///
/// Pokémon to modify.
/// Desired value to set.
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;
}
}
///
/// Sets the value by sanity checking the provided against the possible pool of abilities.
///
/// Pokémon to modify.
/// Desired to set.
public static void SetAbility(this PKM pk, int abil)
{
if (abil < 0)
return;
var abilities = pk.PersonalInfo.Abilities;
int abilIndex = Array.IndexOf(abilities, abil);
abilIndex = Math.Max(0, abilIndex);
pk.SetAbilityIndex(abilIndex);
}
///
/// Sets the value based on the provided ability index (0-2)
///
/// Pokémon to modify.
/// Desired (shifted by 1) to set.
public static void SetAbilityIndex(this PKM pk, int abilIndex)
{
if (pk is PK5 pk5 && abilIndex == 2)
pk5.HiddenAbility = true;
else if (pk.Format <= 5)
pk.PID = PKX.GetRandomPID(Util.Rand, pk.Species, pk.Gender, pk.Version, pk.Nature, pk.AltForm, (uint)(abilIndex * 0x10001));
pk.RefreshAbility(abilIndex);
}
///
/// Sets a Random value. The is not updated if the value should match the instead.
///
/// Accounts for Wurmple evolutions.
/// Pokémon to modify.
public static void SetRandomEC(this PKM pk)
{
int gen = pk.GenNumber;
if (2 < gen && gen < 6)
{
pk.EncryptionConstant = pk.PID;
return;
}
int wIndex = WurmpleUtil.GetWurmpleEvoGroup(pk.Species);
if (wIndex != -1)
{
pk.EncryptionConstant = WurmpleUtil.GetWurmpleEC(wIndex);
return;
}
pk.EncryptionConstant = Util.Rand32();
}
///
/// Sets the derived value.
///
/// Pokémon to modify.
/// Desired state to set.
///
public static bool SetIsShiny(this PKM pk, bool shiny) => shiny ? SetShiny(pk) : pk.SetUnshiny();
///
/// Makes a shiny.
///
/// Pokémon to modify.
/// Square shiny
/// Returns true if the data was modified.
public static bool SetShiny(PKM pk, bool xor0 = false)
{
if (pk.IsShiny)
return false;
while (true)
{
pk.SetShiny();
if (pk.Format <= 7)
return true;
var xor = pk.ShinyXor;
if (xor0 ? xor == 0 : xor != 0)
return true;
}
}
///
/// Makes a not-shiny.
///
/// Pokémon to modify.
/// Returns true if the data was modified.
public static bool SetUnshiny(this PKM pk)
{
if (!pk.IsShiny)
return false;
pk.SetPIDGender(pk.Gender);
return true;
}
///
/// Sets the value, with special consideration for the values which derive the value.
///
/// Pokémon to modify.
/// Desired value to set.
public static void SetNature(this PKM pk, int nature)
{
var value = Math.Min((int)Nature.Quirky, Math.Max((int)Nature.Hardy, nature));
if (pk.Format >= 8)
pk.StatNature = value;
else if (pk.Format <= 4)
pk.SetPIDNature(value);
else
pk.Nature = value;
}
///
/// Copies details to the .
///
/// Pokémon to modify.
/// details to copy from.
public static void ApplySetDetails(this PKM pk, IBattleTemplate Set)
{
pk.Species = Math.Min(pk.MaxSpeciesID, Set.Species);
pk.SetMoves(Set.Moves, true);
pk.ApplyHeldItem(Set.HeldItem, Set.Format);
pk.CurrentLevel = Set.Level;
pk.CurrentFriendship = Set.Friendship;
pk.IVs = Set.IVs;
pk.EVs = Set.EVs;
pk.SetSuggestedHyperTrainingData(Set.IVs);
if (ShowdownSetIVMarkings)
pk.SetMarkings();
pk.SetNickname(Set.Nickname);
pk.SetAltForm(Set.FormIndex);
pk.SetGender(Set.Gender);
pk.SetMaximumPPUps(Set.Moves);
pk.SetAbility(Set.Ability);
pk.SetNature(Set.Nature);
pk.SetIsShiny(Set.Shiny);
pk.SetRandomEC();
if (pk is IAwakened a)
{
a.SetSuggestedAwakenedValues(pk);
if (pk is PB7 b)
{
for (int i = 0; i < 6; i++)
pk.SetEV(i, 0);
b.ResetCalculatedValues();
}
}
if (pk is IGigantamax c)
c.CanGigantamax = Set.CanGigantamax;
pk.ClearRecordFlags();
pk.SetRecordFlags(Set.Moves);
if (ShowdownSetBehaviorNature && pk.Format >= 8)
pk.Nature = pk.StatNature;
var legal = new LegalityAnalysis(pk);
if (legal.Parsed && legal.Info.Relearn.Any(z => !z.Valid))
pk.SetRelearnMoves(legal.GetSuggestedRelearnMoves());
pk.ResetPartyStats();
pk.RefreshChecksum();
}
///
/// Sets the value depending on the current format and the provided item index & format.
///
/// Pokémon to modify.
/// Held Item to apply
/// Format required for importing
public static void ApplyHeldItem(this PKM pk, int item, int format)
{
item = ItemConverter.GetFormatHeldItemID(item, format, pk.Format);
pk.HeldItem = ((uint)item > pk.MaxItemID) ? 0 : item;
}
///
/// Sets one of the based on its index within the array.
///
/// Pokémon to modify.
/// Index to set to
/// Value to set
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));
}
}
///
/// Sets one of the based on its index within the array.
///
/// Pokémon to modify.
/// Index to set to
/// Value to set
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));
}
}
///
/// Fetches the highest value the provided index can be while considering others.
///
/// Pokémon to modify.
/// Index to fetch for
/// Highest value the value can be.
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;
return Math.Min(Math.Max(remaining, 0), 252);
}
///
/// Fetches the highest value the provided .
///
/// Pokémon to modify.
/// Index to fetch for
/// Causes the returned value to be dropped down -1 if the value is already at a maxmimum.
/// Highest value the value can be.
public static int GetMaximumIV(this PKM pk, int index, bool allow30 = false)
{
if (pk.GetIV(index) == pk.MaxIV && allow30)
return pk.MaxIV - 1;
return pk.MaxIV;
}
///
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
///
/// PKM to apply hatch details to
/// Re-hatch already hatched inputs
public static void ForceHatchPKM(this PKM pk, bool reHatch = false)
{
if (!pk.IsEgg && !reHatch)
return;
pk.IsEgg = false;
pk.ClearNickname();
pk.CurrentFriendship = pk.PersonalInfo.BaseFriendship;
if (pk.IsTradedEgg)
pk.Egg_Location = pk.Met_Location;
var loc = EncounterSuggestion.GetSuggestedEggMetLocation(pk);
if (loc >= 0)
pk.Met_Location = loc;
pk.MetDate = DateTime.Today;
if (pk.Gen6)
pk.SetHatchMemory6();
}
///
/// Force hatches a PKM by applying the current species name and a valid Met Location from the origin game.
///
/// PKM to apply hatch details to
/// Game the egg originated from
/// Game the egg is currently present on
public static void SetEggMetData(this PKM pk, GameVersion origin, GameVersion dest)
{
bool traded = origin == dest;
var today = pk.MetDate = DateTime.Today;
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk, traded);
pk.EggMetDate = today;
}
///
/// Maximizes the . If the , the hatch counter is set to 1.
///
/// PKM to apply hatch details to
public static void MaximizeFriendship(this PKM pk)
{
if (pk.IsEgg)
pk.OT_Friendship = 1;
else
pk.CurrentFriendship = byte.MaxValue;
if (pk is PB7 pb)
pb.ResetCP();
}
///
/// Maximizes the . If the , the is ignored.
///
/// PKM to apply hatch details to
public static void MaximizeLevel(this PKM pk)
{
if (pk.IsEgg)
return;
pk.CurrentLevel = 100;
if (pk is PB7 pb)
pb.ResetCP();
}
///
/// Sets the to its default value.
///
/// Pokémon to modify.
/// Precomputed optional
public static void SetDefaultNickname(this PKM pk, LegalityAnalysis la)
{
if (la.Parsed && la.EncounterOriginal is EncounterTrade t && t.HasNickname)
pk.SetNickname(t.GetNickname(pk.Language));
else
pk.ClearNickname();
}
///
/// Sets the to its default value.
///
/// Pokémon to modify.
public static void SetDefaultNickname(this PKM pk) => pk.SetDefaultNickname(new LegalityAnalysis(pk));
private static readonly string[] PotentialUnicode = { "★☆☆☆", "★★☆☆", "★★★☆", "★★★★" };
private static readonly string[] PotentialNoUnicode = { "+", "++", "+++", "++++" };
///
/// Gets the Potential evaluation of the input .
///
/// Pokémon to analyze.
/// Returned value is unicode or not
/// Potential string
public static string GetPotentialString(this PKM pk, bool unicode = true)
{
var arr = unicode ? PotentialUnicode : PotentialNoUnicode;
return arr[pk.PotentialRating];
}
// Extensions
///
/// Gets the Location Name for the
///
/// PKM to fetch data for
/// Location requested is the egg obtained location, not met location.
/// Location string
public static string GetLocationString(this PKM pk, bool eggmet)
{
if (pk.Format < 2)
return string.Empty;
int locval = eggmet ? pk.Egg_Location : pk.Met_Location;
return GameInfo.GetLocationName(eggmet, locval, pk.Format, pk.GenNumber, (GameVersion)pk.Version);
}
}
}