Remove PersonalInfo.Abilities, use interfaces

If you must get a list of abilities, then use the span extension methods.
Also reworks some initial moveset fetching to allocate less
This commit is contained in:
Kurt 2022-09-02 10:20:19 -07:00
parent a97e74e628
commit 1960b335fd
39 changed files with 321 additions and 466 deletions

View file

@ -56,7 +56,7 @@ public static class CommonEdits
{
if (abil < 0)
return;
var index = pk.PersonalInfo.GetAbilityIndex(abil);
var index = pk.PersonalInfo.GetIndexOfAbility(abil);
index = Math.Max(0, index);
pk.SetAbilityIndex(index);
}

View file

@ -80,21 +80,18 @@ public sealed class FilteredGameDataSource
public IReadOnlyList<ComboItem> GetAbilityList(PKM pk)
{
var abilities = pk.PersonalInfo.Abilities;
int format = pk.Format;
return GetAbilityList(abilities, format);
return GetAbilityList(pk.PersonalInfo, pk.Format);
}
public IReadOnlyList<ComboItem> GetAbilityList(IReadOnlyList<int> abilities, int format)
public IReadOnlyList<ComboItem> GetAbilityList(IPersonalAbility pi, int format)
{
var count = format == 3 && (abilities[1] == 0 || abilities[1] == abilities[0]) ? 1 : abilities.Count;
var list = new ComboItem[count];
var list = new ComboItem[pi.AbilityCount];
var alist = Source.Strings.Ability;
var suffix = AbilityIndexSuffixes;
for (int i = 0; i < list.Length; i++)
{
var ability = abilities[i];
var ability = pi.GetAbilityAtIndex(i);
list[i] = new ComboItem(alist[ability] + suffix[i], ability);
}

View file

@ -124,7 +124,8 @@ public abstract record EncounterSlot(EncounterArea Area, ushort Species, byte Fo
protected virtual void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = MoveLevelUp.GetEncounterMoves(pk, level, version);
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, version);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -1,3 +1,5 @@
using System;
namespace PKHeX.Core;
/// <summary>
@ -47,7 +49,8 @@ public sealed record EncounterSlot7GO : EncounterSlotGO
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = MoveLevelUp.GetEncounterMoves(pk, level, GameVersion.GG);
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, GameVersion.GG);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -120,9 +120,8 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship
pk.Gender = gender;
pk.AbilityNumber = 1 << ability;
var abilities = pi.Abilities;
if ((uint)ability < abilities.Count)
pk.Ability = abilities[ability];
if ((uint)ability < pi.AbilityCount)
pk.Ability = pi.GetAbilityAtIndex(ability);
pk.SetRandomIVsGO();
base.SetPINGA(pk, criteria);
@ -130,12 +129,20 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = GetInitialMoves(level);
Span<ushort> moves = stackalloc ushort[4];
GetInitialMoves(level, moves);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}
public ReadOnlySpan<ushort> GetInitialMoves(int level) => MoveLevelUp.GetEncounterMoves(Species, Form, level, OriginGroup);
public void GetInitialMoves(int level, Span<ushort> moves) => MoveLevelUp.GetEncounterMoves(moves, Species, Form, level, OriginGroup);
public ReadOnlySpan<ushort> GetInitialMoves(int level)
{
var result = new ushort[4];
GetInitialMoves(level, result);
return result;
}
public override EncounterMatchRating GetMatchRating(PKM pk)
{

View file

@ -165,7 +165,8 @@ public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IM
}
else
{
var moves = MoveLevelUp.GetEncounterMoves(pk, level, version);
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, version);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -50,12 +50,12 @@ public abstract record EncounterStatic8Nest<T>(GameVersion Version) : EncounterS
var num = pk.AbilityNumber;
if (num == 4)
{
if (Ability is not OnlyHidden && !AbilityVerifier.CanAbilityPatch(8, PersonalTable.SWSH.GetFormEntry(Species, Form).Abilities, pk.Species))
if (Ability is not OnlyHidden && !AbilityVerifier.CanAbilityPatch(8, PersonalTable.SWSH.GetFormEntry(Species, Form), pk.Species))
return EncounterMatchRating.DeferredErrors;
}
else if (Ability.IsSingleValue(out int index) && 1 << index != num) // Fixed regular ability
{
if (Ability is OnlyFirst or OnlySecond && !AbilityVerifier.CanAbilityCapsule(8, PersonalTable.SWSH.GetFormEntry(Species, Form).Abilities))
if (Ability is OnlyFirst or OnlySecond && !AbilityVerifier.CanAbilityCapsule(8, PersonalTable.SWSH.GetFormEntry(Species, Form)))
return EncounterMatchRating.DeferredErrors;
}
}

View file

@ -165,7 +165,8 @@ public abstract record EncounterTrade(GameVersion Version) : IEncounterable, IMo
}
else
{
var moves = MoveLevelUp.GetEncounterMoves(pk, level, version);
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, level, version);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.AbilityPermission;
namespace PKHeX.Core;
@ -106,22 +105,22 @@ public sealed record EncounterCriteria
private static AbilityPermission GetAbilityNumber(int ability, IPersonalAbility pi)
{
var abilities = pi.Abilities;
if (abilities.Count < 2)
return 0;
var dual = GetAbilityValueDual(ability, abilities);
if (abilities.Count == 2) // prior to gen5
var count = pi.AbilityCount;
if (count < 2 || pi is not IPersonalAbility12 a)
return Any12;
var dual = GetAbilityValueDual(ability, a);
if (count == 2 || pi is not IPersonalAbility12H h) // prior to gen5
return dual;
if (abilities[2] == ability)
return dual == 0 ? Any12H : OnlyHidden;
if (ability == h.AbilityH)
return dual == Any12 ? Any12H : OnlyHidden;
return dual;
}
private static AbilityPermission GetAbilityValueDual(int ability, IReadOnlyList<int> abilities)
private static AbilityPermission GetAbilityValueDual(int ability, IPersonalAbility12 a)
{
if (ability == abilities[0])
return ability != abilities[1] ? OnlyFirst : Any12;
return ability == abilities[1] ? OnlySecond : Any12;
if (ability == a.Ability1)
return ability != a.Ability2 ? OnlyFirst : Any12;
return ability == a.Ability2 ? OnlySecond : Any12;
}
/// <summary>

View file

@ -36,7 +36,9 @@ public static class LearnPossible
return;
if (enc is EncounterSlot8GO g)
{
SetAll(g.GetInitialMoves(pk.Met_Level), result);
Span<ushort> initial = stackalloc ushort[4];
g.GetInitialMoves(pk.Met_Level, initial);
SetAll(initial, result);
}
else if (enc.Generation >= 6)
{

View file

@ -49,10 +49,16 @@ internal static class LearnVerifierHistory
private static void MarkSpecialMoves(Span<MoveResult> result, ReadOnlySpan<ushort> current, IEncounterTemplate enc, PKM pk)
{
if (enc is IMoveset { Moves: {HasMoves: true} moves})
if (enc is IMoveset { Moves: { HasMoves: true } moves })
{
MarkInitialMoves(result, current, moves);
}
else if (enc is EncounterSlot8GO g)
MarkInitialMoves(result, current, g.GetInitialMoves(pk.Met_Level));
{
Span<ushort> initial = stackalloc ushort[4];
g.GetInitialMoves(pk.Met_Level, initial);
MarkInitialMoves(result, current, initial);
}
}
private static bool Iterate(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvolutionHistory history, IEncounterTemplate enc, ILearnGroup game, MoveSourceType types, LearnOption option)

View file

@ -188,19 +188,6 @@ public sealed class Learnset
}
}
public IList<int> GetUniqueMovesLearned(IEnumerable<int> seed, int maxLevel, int minLevel = 0)
{
int start = Array.FindIndex(Levels, z => z >= minLevel);
int end = Array.FindLastIndex(Levels, z => z <= maxLevel);
var list = new List<int>(seed);
for (int i = start; i <= end; i++)
{
if (!list.Contains(Moves[i]))
list.Add(Moves[i]);
}
return list;
}
/// <summary>Returns the index of the lowest level move if the Pokémon were encountered at the specified level.</summary>
/// <remarks>Helps determine the minimum level an encounter can be at.</remarks>
/// <param name="level">The level the Pokémon was encountered at.</param>

View file

@ -6,48 +6,45 @@ namespace PKHeX.Core;
public static class MoveLevelUp
{
public static ushort[] GetEncounterMoves(PKM pk, int level, GameVersion version)
public static void GetEncounterMoves(Span<ushort> moves, PKM pk, int level, GameVersion version)
{
if (version <= 0)
version = (GameVersion)pk.Version;
return GetEncounterMoves(pk.Species, pk.Form, level, version);
GetEncounterMoves(moves, pk.Species, pk.Form, level, version);
}
private static ushort[] GetEncounterMoves1(ushort species, int level, GameVersion version)
private static void GetEncounterMoves1(Span<ushort> result, ushort species, int level, GameVersion version)
{
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var table = version is YW or RBY ? PersonalTable.Y : PersonalTable.RB;
var index = table.GetFormIndex(species, 0);
Span<ushort> lvl0 = stackalloc ushort[4];
((PersonalInfo1) table[index]).GetMoves(lvl0);
int start = Math.Max(0, lvl0.IndexOf((ushort)0));
// The initial moves are seeded from Personal rather than learn.
table[index].GetMoves(result);
int start = Math.Max(0, result.IndexOf((ushort)0));
learn[index].SetEncounterMoves(level, lvl0, start);
return lvl0.ToArray();
learn[index].SetEncounterMoves(level, result, start);
}
private static ushort[] GetEncounterMoves2(ushort species, int level, GameVersion version)
public static void GetEncounterMoves(Span<ushort> result, ushort species, byte form, int level, GameVersion version)
{
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var index = table.GetFormIndex(species, 0);
var lvl0 = learn[species].GetEncounterMoves(1);
int start = Math.Max(0, Array.IndexOf(lvl0, (ushort)0));
learn[index].SetEncounterMoves(level, lvl0, start);
return lvl0;
if (RBY.Contains(version))
{
GetEncounterMoves1(result, species, level, version);
}
else
{
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var index = table.GetFormIndex(species, form);
learn[index].SetEncounterMoves(level, result);
}
}
public static ushort[] GetEncounterMoves(ushort species, byte form, int level, GameVersion version)
{
if (RBY.Contains(version))
return GetEncounterMoves1(species, level, version);
if (GSC.Contains(version))
return GetEncounterMoves2(species, level, version);
var learn = GameData.GetLearnsets(version);
var table = GameData.GetPersonal(version);
var index = table.GetFormIndex(species, form);
return learn[index].GetEncounterMoves(level);
var result = new ushort[4];
GetEncounterMoves(result, species, form, level, version);
return result;
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
@ -29,34 +28,33 @@ public sealed class AbilityVerifier : Verifier
private CheckResult VerifyAbility(LegalityAnalysis data)
{
var pk = data.Entity;
var pi = data.PersonalInfo;
var abilities = (IPersonalAbility12)data.PersonalInfo;
// Check ability is possible (within bounds)
int ability = pk.Ability;
int abilIndex = pi.GetAbilityIndex(ability);
int abilIndex = abilities.GetIndexOfAbility(ability);
if (abilIndex < 0)
return GetInvalid(LAbilityUnexpected);
var abilities = pi.Abilities;
int format = pk.Format;
if (format >= 6)
{
var num = pk.AbilityNumber;
if (!IsValidAbilityBits(num))
var bitNum = pk.AbilityNumber;
if (!IsValidAbilityBits(bitNum))
return INVALID;
// Check AbilityNumber points to ability
int an = num >> 1;
if (an >= abilities.Count || abilities[an] != ability)
int abilityIndex = bitNum >> 1;
if (abilityIndex >= abilities.AbilityCount || abilities.GetAbilityAtIndex(abilityIndex) != ability)
return INVALID;
// Check AbilityNumber for transfers without unique abilities
int gen = data.Info.Generation;
if (gen is 3 or 4 or 5 && num != 4)
if (gen is 3 or 4 or 5 && bitNum != 4)
{
// To determine AbilityNumber [PK5->PK6], check if the first ability in Personal matches the ability.
// It is not possible to flip it to the other index as capsule requires unique abilities.
if (abilities[0] == abilities[1] && num != 1)
if (abilities.GetIsAbility12Same() && bitNum != 1)
{
// Check if any pre-evolution could have it flipped.
var evos = data.Info.EvoChainsAllGens.Gen6;
@ -70,14 +68,18 @@ public sealed class AbilityVerifier : Verifier
if (format >= 8) // Ability Patch
{
var evos = data.Info.EvoChainsAllGens;
if (pk.AbilityNumber == 4 && IsAccessibleAbilityPatch(evos))
if (pk.AbilityNumber == 4 && IsAccessibleAbilityPatch(evos) && abilities is IPersonalAbility12H h)
{
if (CanAbilityPatch(format, abilities, pk.Species))
if (CanAbilityPatch(format, h, pk.Species))
return GetValid(LAbilityPatchUsed);
var e = data.EncounterOriginal;
if (e.Species != pk.Species && CanAbilityPatch(format, PKX.Personal.GetFormEntry(e.Species, e.Form).Abilities, e.Species))
return GetValid(LAbilityPatchUsed);
if (e.Species != pk.Species)
{
var temp = (IPersonalAbility12H)PKX.Personal.GetFormEntry(e.Species, e.Form);
if (CanAbilityPatch(format, temp, e.Species))
return GetValid(LAbilityPatchUsed);
}
// Verify later, it may be encountered with its hidden ability without using an ability patch.
}
@ -93,7 +95,7 @@ public sealed class AbilityVerifier : Verifier
return VerifyAbility(data, abilities, abilIndex);
}
public static bool IsValidAbilityBits(int num) => num is 1 or 2 or 4;
public static bool IsValidAbilityBits(int bitNum) => bitNum is 1 or 2 or 4;
private static bool GetWasDual(ReadOnlySpan<EvoCriteria> evos, IPersonalTable pt, ISpeciesForm pk)
{
@ -102,16 +104,15 @@ public sealed class AbilityVerifier : Verifier
if (evo.Species == pk.Species)
continue;
var pe = pt.GetFormEntry(evo.Species, evo.Form);
var abils = pe.Abilities;
if (CanAbilityCapsule(6, abils))
var abilities = (IPersonalAbility12)pt.GetFormEntry(evo.Species, evo.Form);
if (CanAbilityCapsule(6, abilities))
return true;
}
return false;
}
private CheckResult VerifyAbility(LegalityAnalysis data, IReadOnlyList<int> abilities, int abilIndex)
private CheckResult VerifyAbility(LegalityAnalysis data, IPersonalAbility12 abilities, int abilIndex)
{
var enc = data.EncounterMatch;
var eabil = enc.Ability;
@ -135,12 +136,12 @@ public sealed class AbilityVerifier : Verifier
};
}
private CheckResult VerifyAbility345(LegalityAnalysis data, IEncounterable enc, IReadOnlyList<int> abilities, int abilIndex)
private CheckResult VerifyAbility345(LegalityAnalysis data, IEncounterable enc, IPersonalAbility12 abilities, int abilIndex)
{
var pk = data.Entity;
int format = pk.Format;
var state = AbilityState.MustMatch;
if (format is (3 or 4 or 5) && abilities[0] != abilities[1]) // 3-4/5 and have 2 distinct abilities now
if (format is (3 or 4 or 5) && !abilities.GetIsAbility12Same()) // 3-4/5 and have 2 distinct abilities now
state = VerifyAbilityPreCapsule(data, abilities);
var encounterAbility = enc.Ability;
@ -159,7 +160,7 @@ public sealed class AbilityVerifier : Verifier
return CheckMatch(pk, abilities, gen, state, enc);
}
private CheckResult VerifyFixedAbility(LegalityAnalysis data, IReadOnlyList<int> abilities, AbilityState state, AbilityPermission encounterAbility, int abilIndex)
private CheckResult VerifyFixedAbility(LegalityAnalysis data, IPersonalAbility12 abilities, AbilityState state, AbilityPermission encounterAbility, int abilIndex)
{
var pk = data.Entity;
var enc = data.Info.EncounterMatch;
@ -210,7 +211,7 @@ public sealed class AbilityVerifier : Verifier
return INVALID;
}
private AbilityState VerifyAbilityPreCapsule(LegalityAnalysis data, IReadOnlyList<int> abilities)
private AbilityState VerifyAbilityPreCapsule(LegalityAnalysis data, IPersonalAbility12 abilities)
{
var info = data.Info;
// Gen4/5 origin
@ -242,7 +243,7 @@ public sealed class AbilityVerifier : Verifier
return VerifyAbilityGen3Transfer(data, abilities, maxGen3Species);
}
private AbilityState VerifyAbilityGen3Transfer(LegalityAnalysis data, IReadOnlyList<int> abilities, int maxGen3Species)
private AbilityState VerifyAbilityGen3Transfer(LegalityAnalysis data, IPersonalAbility12 abilities, int maxGen3Species)
{
var pk = data.Entity;
var pers = PersonalTable.E[maxGen3Species];
@ -259,7 +260,7 @@ public sealed class AbilityVerifier : Verifier
if (pk.Ability == pers.Ability1) // Could evolve in Gen4/5 and have a Gen3 only ability
return AbilityState.CanMismatch; // Not evolved in Gen4/5, doesn't need to match PIDAbility
if (pk.Ability == abilities[1]) // It could evolve in Gen4/5 and have Gen4 second ability
if (pk.Ability == abilities.Ability2) // It could evolve in Gen4/5 and have Gen4 second ability
return AbilityState.MustMatch; // Evolved in Gen4/5, must match PIDAbility
}
@ -271,7 +272,7 @@ public sealed class AbilityVerifier : Verifier
return AbilityState.CanMismatch;
}
private CheckResult VerifyAbilityMG(LegalityAnalysis data, MysteryGift g, IReadOnlyList<int> abilities)
private CheckResult VerifyAbilityMG(LegalityAnalysis data, MysteryGift g, IPersonalAbility12 abilities)
{
if (g is PCD d)
return VerifyAbilityPCD(data, abilities, d);
@ -312,7 +313,7 @@ public sealed class AbilityVerifier : Verifier
return pk.Format < 6 ? GetInvalid(LAbilityMismatchPID) : INVALID;
}
private CheckResult VerifyAbilityPCD(LegalityAnalysis data, IReadOnlyList<int> abilities, PCD pcd)
private CheckResult VerifyAbilityPCD(LegalityAnalysis data, IPersonalAbility12 abilities, PCD pcd)
{
var pk = data.Entity;
var format = pk.Format;
@ -336,7 +337,7 @@ public sealed class AbilityVerifier : Verifier
return pk.Ability == pcd.Gift.PK.Ability ? VALID : INVALID;
}
private CheckResult VerifyAbility5(LegalityAnalysis data, IEncounterTemplate enc, IReadOnlyList<int> abilities)
private CheckResult VerifyAbility5(LegalityAnalysis data, IEncounterTemplate enc, IPersonalAbility12 abilities)
{
var pk = data.Entity;
@ -396,7 +397,7 @@ public sealed class AbilityVerifier : Verifier
/// <param name="gen">Generation</param>
/// <param name="state">Permissive to allow ability to deviate under special circumstances</param>
/// <param name="enc">Encounter template the <see cref="pk"/> was matched to.</param>
private CheckResult CheckMatch(PKM pk, IReadOnlyList<int> abilities, int gen, AbilityState state, IEncounterTemplate enc)
private CheckResult CheckMatch(PKM pk, IPersonalAbility12 abilities, int gen, AbilityState state, IEncounterTemplate enc)
{
if (gen is (3 or 4) && pk.AbilityNumber == 4)
return GetInvalid(LAbilityHiddenUnavailable);
@ -411,7 +412,7 @@ public sealed class AbilityVerifier : Verifier
var abit = g3.AbilityBit;
// We've sanitized our personal data to replace "None" abilities with the first ability.
// Granbull, Vibrava, and Flygon have dual abilities being the same.
if (abilities[0] == abilities[1] && g3.Species is not ((int)Species.Granbull or (int)Species.Vibrava or (int)Species.Flygon)) // Not a dual ability
if (abilities.GetIsAbility12Same() && g3.Species is not ((int)Species.Granbull or (int)Species.Vibrava or (int)Species.Flygon)) // Not a dual ability
{
// Must not have the Ability bit flag set.
// Shadow encounters set a random ability index; don't bother checking if it's a re-battle for ability bit flipping.
@ -437,10 +438,11 @@ public sealed class AbilityVerifier : Verifier
return GetPIDAbilityMatch(pk, abilities);
}
private CheckResult GetPIDAbilityMatch(PKM pk, IReadOnlyList<int> abilities)
private CheckResult GetPIDAbilityMatch(PKM pk, IPersonalAbility abilities)
{
// Ability Number bits are already verified as clean.
var abil = abilities[pk.AbilityNumber >> 1];
var index = pk.AbilityNumber >> 1;
var abil = abilities.GetAbilityAtIndex(index);
if (abil != pk.Ability)
return GetInvalid(LAbilityMismatchPID);
@ -460,7 +462,7 @@ public sealed class AbilityVerifier : Verifier
}
// Ability Capsule can change between 1/2
private static bool IsAbilityCapsuleModified(PKM pk, IReadOnlyList<int> abilities, AbilityPermission encounterAbility, EvolutionHistory evos)
private static bool IsAbilityCapsuleModified(PKM pk, IPersonalAbility12 abilities, AbilityPermission encounterAbility, EvolutionHistory evos)
{
if (!IsAccessibleAbilityCapsule(evos))
return false; // Not available.
@ -473,21 +475,20 @@ public sealed class AbilityVerifier : Verifier
return true;
}
public static bool CanAbilityCapsule(int format, IReadOnlyList<int> abilities)
public static bool CanAbilityCapsule(int format, IPersonalAbility12 abilities)
{
if (format < 6) // Ability Capsule does not exist
return false;
return abilities[0] != abilities[1]; // Cannot alter ability index if it is the same as the other ability.
return !abilities.GetIsAbility12Same(); // Cannot alter ability index if it is the same as the other ability.
}
public static bool CanAbilityPatch(int format, IReadOnlyList<int> abilities, ushort species)
public static bool CanAbilityPatch(int format, IPersonalAbility12H abilities, ushort species)
{
if (format < 8) // Ability Patch does not exist
return false;
// Can alter ability index if it is different from the other abilities.
var h = abilities[2];
if (h != abilities[0] || h != abilities[1])
if (abilities.GetIsAbilityPatchPossible())
return true;
// Some species have a distinct hidden ability only on another form, and can change between that form and its current form.

View file

@ -448,7 +448,8 @@ public sealed class BallVerifier : Verifier
{
if (pk.AbilityNumber != 4)
return false;
return !AbilityVerifier.CanAbilityPatch(pk.Format, pk.PersonalInfo.Abilities, pk.Species);
var abilities = (IPersonalAbility12H)pk.PersonalInfo;
return !AbilityVerifier.CanAbilityPatch(pk.Format, abilities, pk.Species);
}
private static bool IsGalarCatchAndBreed(ushort species)

View file

@ -73,9 +73,15 @@ public sealed class IndividualValueVerifier : Verifier
{
var pk = data.Entity;
if (pk.GO)
{
VerifyIVsGoTransfer(data);
else if (pk.AbilityNumber == 4 && !AbilityVerifier.CanAbilityPatch(pk.Format, pk.PersonalInfo.Abilities, pk.Species))
VerifyIVsFlawless(data, 2); // Chain of 10 yields 5% HA and 2 flawless IVs
}
else if (pk.AbilityNumber == 4)
{
var abilities = (IPersonalAbility12H)pk.PersonalInfo;
if (!AbilityVerifier.CanAbilityPatch(pk.Format, abilities, pk.Species))
VerifyIVsFlawless(data, 2); // Chain of 10 yields 5% HA and 2 flawless IVs
}
}
private void VerifyIVsGen8(LegalityAnalysis data)

View file

@ -242,7 +242,11 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
pk.Version = (int)GameVersion.W + rnd.Next(4);
if (Move1 == 0) // No moves defined
pk.Moves = MoveLevelUp.GetEncounterMoves(Species, Form, Level, (GameVersion)pk.Version);
{
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, Species, Form, Level, (GameVersion)pk.Version);
pk.SetMoves(moves);
}
pk.SetMaximumPPCurrent();

View file

@ -1,3 +1,5 @@
using System;
namespace PKHeX.Core;
/// <summary>
@ -28,9 +30,12 @@ public static class BattleVersionExtensions
/// <param name="version">Version to apply</param>
public static void AdaptToBattleVersion(this IBattleVersion v, PKM pk, GameVersion version)
{
var moves = MoveLevelUp.GetEncounterMoves(pk, pk.CurrentLevel, version);
pk.Move1 = pk.Move2 = pk.Move3 = pk.Move4 = 0;
pk.RelearnMove1 = pk.RelearnMove2 = pk.RelearnMove3 = pk.RelearnMove4 = 0;
var empty = new Moveset();
pk.SetMoves(empty);
pk.SetRelearnMoves(empty);
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, pk.CurrentLevel, version);
pk.SetMoves(moves);
pk.FixMoves();
v.BattleVersion = (byte) version;

View file

@ -499,8 +499,10 @@ public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
{
case 1 or 2 or 4: // Valid Ability Numbers
int index = an >> 1;
if (PersonalInfo.Abilities[index] == Ability) // correct pair
pk7.Ability = pk7.PersonalInfo.Abilities[index];
var abilities = (IPersonalAbility12H)PersonalInfo;
if (abilities.GetAbilityAtIndex(index) == Ability) // correct pair
pk7.Ability = pk7.PersonalInfo.GetAbilityAtIndex(index);
break;
}

View file

@ -503,7 +503,7 @@ public abstract class PKM : ISpeciesForm, ITrainerID, IGeneration, IShiny, ILang
return -1;
if (Version == (int) CXD)
return PersonalInfo.GetAbilityIndex(Ability); // Can mismatch; not tied to PID
return PersonalInfo.GetIndexOfAbility(Ability); // Can mismatch; not tied to PID
return (int)((Gen5 ? PID >> 16 : PID) & 1);
}
}
@ -630,9 +630,9 @@ public abstract class PKM : ISpeciesForm, ITrainerID, IGeneration, IShiny, ILang
public virtual void RefreshAbility(int n)
{
AbilityNumber = 1 << n;
var abilities = PersonalInfo.Abilities;
if ((uint)n < abilities.Count)
Ability = abilities[n];
IPersonalAbility pi = PersonalInfo;
if ((uint)n < pi.AbilityCount)
Ability = pi.GetAbilityAtIndex(n);
}
/// <summary>

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
@ -53,8 +52,9 @@ public sealed class PersonalInfo1 : PersonalInfo
// Future game values, unused
public override int EggGroup1 { get => 0; set { } }
public override int EggGroup2 { get => 0; set { } }
public override IReadOnlyList<int> Abilities { get => Array.Empty<int>(); set { } }
public override int GetAbilityIndex(int abilityID) => -1;
public override int GetIndexOfAbility(int abilityID) => -1;
public override int GetAbilityAtIndex(int abilityIndex) => -1;
public override int AbilityCount => 0;
public override int Gender { get; set; }
public override int HatchCycles { get => 0; set { } }
public override int BaseFriendship { get => 0; set { } }

View file

@ -62,8 +62,9 @@ public sealed class PersonalInfo2 : PersonalInfo
public override int EV_SPD { get => SPD; set { } }
// Future game values, unused
public override IReadOnlyList<int> Abilities { get => Array.Empty<int>(); set { } }
public override int GetAbilityIndex(int abilityID) => -1;
public override int GetIndexOfAbility(int abilityID) => -1;
public override int GetAbilityAtIndex(int abilityIndex) => -1;
public override int AbilityCount => 0;
public override int BaseFriendship { get => 70; set { } }
public override int EscapeRate { get => 0; set { } }
public override int Color { get => 0; set { } }

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from Generation 3 games.
/// </summary>
public sealed class PersonalInfo3 : PersonalInfo
public sealed class PersonalInfo3 : PersonalInfo, IPersonalAbility12
{
public const int SIZE = 0x1C;
private readonly byte[] Data;
@ -47,29 +46,14 @@ public sealed class PersonalInfo3 : PersonalInfo
public override int Color { get => Data[0x19] & 0x7F; set => Data[0x19] = (byte)((Data[0x19] & 0x80) | value); }
public bool NoFlip { get => Data[0x19] >> 7 == 1; set => Data[0x19] = (byte)(Color | (value ? 0x80 : 0)); }
public IReadOnlyList<int> Items
public override int AbilityCount => 2;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2 };
set
{
if (value.Count != 2) return;
Item1 = value[0];
Item2 = value[1];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2 };
set
{
if (value.Count != 2) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : -1;
0 => Ability1,
1 => Ability2,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
public int GetAbility(bool second) => second && HasSecondAbility ? Ability2 : Ability1;
public bool HasSecondAbility => Ability1 != Ability2;

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from Generation 4 games.
/// </summary>
public sealed class PersonalInfo4 : PersonalInfo
public sealed class PersonalInfo4 : PersonalInfo, IPersonalAbility12
{
public const int SIZE = 0x2C;
private readonly byte[] Data;
@ -57,29 +56,14 @@ public sealed class PersonalInfo4 : PersonalInfo
public override int Color { get => Data[0x19] & 0x7F; set => Data[0x19] = (byte)((Data[0x19] & 0x80) | value); }
public bool NoFlip { get => Data[0x19] >> 7 == 1; set => Data[0x19] = (byte)(Color | (value ? 0x80 : 0)); }
public IReadOnlyList<int> Items
public override int AbilityCount => 2;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2 };
set
{
if (value.Count != 2) return;
Item1 = value[0];
Item2 = value[1];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2 };
set
{
if (value.Count != 2) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : -1;
0 => Ability1,
1 => Ability2,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
public int GetAbility(bool second) => second && HasSecondAbility ? Ability2 : Ability1;
public bool HasSecondAbility => Ability1 != Ability2;

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the Black 2 &amp; White 2 games.
/// </summary>
public sealed class PersonalInfo5B2W2 : PersonalInfo
public sealed class PersonalInfo5B2W2 : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0x4C;
private readonly byte[] Data;
@ -72,7 +71,7 @@ public sealed class PersonalInfo5B2W2 : PersonalInfo
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -81,31 +80,15 @@ public sealed class PersonalInfo5B2W2 : PersonalInfo
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
public bool HasHiddenAbility => AbilityH != Ability1;
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -61,7 +60,7 @@ public sealed class PersonalInfo5BW : PersonalInfo, IPersonalAbility12H
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -70,31 +69,13 @@ public sealed class PersonalInfo5BW : PersonalInfo, IPersonalAbility12H
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public bool HasHiddenAbility => AbilityH != Ability1;
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the OR &amp; AS games.
/// </summary>
public sealed class PersonalInfo6AO : PersonalInfo
public sealed class PersonalInfo6AO : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0x50;
private readonly byte[] Data;
@ -73,7 +72,7 @@ public sealed class PersonalInfo6AO : PersonalInfo
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -82,31 +81,13 @@ public sealed class PersonalInfo6AO : PersonalInfo
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public bool HasHiddenAbility => AbilityH != Ability1;
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the X &amp; Y games.
/// </summary>
public sealed class PersonalInfo6XY : PersonalInfo
public sealed class PersonalInfo6XY : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0x40;
private readonly byte[] Data;
@ -62,7 +61,7 @@ public sealed class PersonalInfo6XY : PersonalInfo
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -71,31 +70,13 @@ public sealed class PersonalInfo6XY : PersonalInfo
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public bool HasHiddenAbility => AbilityH != Ability1;
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the Sun &amp; Moon games.
/// </summary>
public sealed class PersonalInfo7 : PersonalInfo
public sealed class PersonalInfo7 : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0x54;
private readonly byte[] Data;
@ -66,7 +65,7 @@ public sealed class PersonalInfo7 : PersonalInfo
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -75,36 +74,18 @@ public sealed class PersonalInfo7 : PersonalInfo
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public bool HasHiddenAbility => AbilityH != Ability1;
public int SpecialZ_Item { get => ReadUInt16LittleEndian(Data.AsSpan(0x4C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4C), (ushort)value); }
public int SpecialZ_BaseMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x4E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4E), (ushort)value); }
public int SpecialZ_ZMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x50)); set => WriteUInt16LittleEndian(Data.AsSpan(0x50), (ushort)value); }
public bool LocalVariant { get => Data[0x52] == 1; set => Data[0x52] = value ? (byte)1 : (byte)0; }
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the <see cref="GameVersion.GG"/> games.
/// </summary>
public sealed class PersonalInfo7GG : PersonalInfo
public sealed class PersonalInfo7GG : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0x54;
private readonly byte[] Data;
@ -60,7 +59,7 @@ public sealed class PersonalInfo7GG : PersonalInfo
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool SpriteFlip { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -75,31 +74,13 @@ public sealed class PersonalInfo7GG : PersonalInfo
public int SpecialZ_ZMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x50)); set => WriteUInt16LittleEndian(Data.AsSpan(0x50), (ushort)value); }
public bool LocalVariant { get => Data[0x52] == 1; set => Data[0x52] = value ? (byte)1 : (byte)0; }
public IReadOnlyList<int> Items
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public bool HasHiddenAbility => AbilityH != Ability1;
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the <see cref="GameVersion.BDSP"/> games.
/// </summary>
public sealed class PersonalInfo8BDSP : PersonalInfo
public sealed class PersonalInfo8BDSP : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0x44;
private const int CountTM = 100;
@ -67,7 +66,7 @@ public sealed class PersonalInfo8BDSP : PersonalInfo
public int AbilityH { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
public override int EscapeRate { get => 0; set { } } // moved?
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public override int BaseEXP { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); }
@ -85,31 +84,15 @@ public sealed class PersonalInfo8BDSP : PersonalInfo
public byte HatchFormIndex { get => (byte)ReadUInt16LittleEndian(Data.AsSpan(0x40)); set => WriteUInt16LittleEndian(Data.AsSpan(0x40), value); }
public ushort PokeDexIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x42)); set => WriteUInt16LittleEndian(Data.AsSpan(0x42), value); }
public IReadOnlyList<int> Items
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = value[0];
Ability2 = value[1];
AbilityH = value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
/// <summary>
/// Checks if the entry shows up in any of the built-in Pokédex.

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the <see cref="GameVersion.PLA"/> games.
/// </summary>
public sealed class PersonalInfo8LA : PersonalInfo
public sealed class PersonalInfo8LA : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0xB0;
private readonly byte[] Data;
@ -66,7 +65,7 @@ public sealed class PersonalInfo8LA : PersonalInfo
public override int EscapeRate { get => 0; set { } } // moved?
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } // ???
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -74,32 +73,6 @@ public sealed class PersonalInfo8LA : PersonalInfo
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = value[0];
Ability2 = value[1];
AbilityH = value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public ushort HatchSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), value); }
public int HatchFormIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), (ushort)value); } // local region base form
public ushort RegionalFlags { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), value); }
@ -113,6 +86,16 @@ public sealed class PersonalInfo8LA : PersonalInfo
public ushort DexIndexLocal4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(0x68), value); }
public ushort DexIndexLocal5 { get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), value); }
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
public int GetMoveShopCount()
{
// Return a count of true indexes from Tutors

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
@ -7,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the <see cref="GameVersion.SWSH"/> games.
/// </summary>
public sealed class PersonalInfo8SWSH : PersonalInfo
public sealed class PersonalInfo8SWSH : PersonalInfo, IPersonalAbility12H
{
public const int SIZE = 0xB0;
public const int CountTM = 100;
@ -86,7 +85,7 @@ public sealed class PersonalInfo8SWSH : PersonalInfo
public override int EscapeRate { get => 0; set { } } // moved?
public override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
public int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } // ???
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override byte FormCount { get => Data[0x20]; set => Data[0x20] = value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
@ -94,32 +93,6 @@ public sealed class PersonalInfo8SWSH : PersonalInfo
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
public IReadOnlyList<int> Items
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = value[0];
Ability2 = value[1];
AbilityH = value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public ushort Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x4C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x4C), value); }
public ushort HatchSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), value); }
@ -141,5 +114,13 @@ public sealed class PersonalInfo8SWSH : PersonalInfo
/// </summary>
public bool IsInDex => PokeDexIndex != 0 || ArmorDexIndex != 0 || CrownDexIndex != 0;
public bool HasHiddenAbility => AbilityH != Ability1;
public override int AbilityCount => 3;
public override int GetIndexOfAbility(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public override int GetAbilityAtIndex(int abilityIndex) => abilityIndex switch
{
0 => Ability1,
1 => Ability2,
2 => AbilityH,
_ => throw new ArgumentOutOfRangeException(nameof(abilityIndex), abilityIndex, null),
};
}

View file

@ -1,23 +1,31 @@
using System.Collections.Generic;
using System;
namespace PKHeX.Core;
public interface IPersonalAbility
{
/// <summary>
/// Full list of <see cref="PKM.Ability"/> values the entry can have.
/// </summary>
IReadOnlyList<int> Abilities { get; set; }
/// <summary>
/// Gets the ability index without creating an array and looking through it.
/// Gets the index of the <see cref="abilityID"/> within the specification's list of abilities.
/// </summary>
/// <param name="abilityID">Ability ID</param>
/// <returns>Ability Index</returns>
int GetAbilityIndex(int abilityID);
int GetIndexOfAbility(int abilityID);
/// <summary>
/// Gets the ability ID at the specified ability index.
/// </summary>
/// <param name="abilityIndex">Ability Index</param>
/// <returns>Ability ID</returns>
int GetAbilityAtIndex(int abilityIndex);
/// <summary>
/// Gets the count of abilities able to be selected.
/// </summary>
/// <remarks>Duplicate abilities still count separately.</remarks>
int AbilityCount { get; }
}
public interface IPersonalAbility12
public interface IPersonalAbility12 : IPersonalAbility
{
int Ability1 { get; set; }
int Ability2 { get; set; }
@ -27,3 +35,32 @@ public interface IPersonalAbility12H : IPersonalAbility12
{
int AbilityH { get; set; }
}
public static class PersonalAbilityExtensions
{
public static bool GetIsAbility12Same(this IPersonalAbility12 pi) => pi.Ability1 == pi.Ability2;
public static bool GetIsAbilityHiddenUnique(this IPersonalAbility12H pi) => pi.Ability1 != pi.AbilityH;
public static bool GetIsAbilityPatchPossible(this IPersonalAbility12H pi) => pi.Ability1 != pi.AbilityH || pi.Ability2 != pi.AbilityH;
public static void GetAbilities(this IPersonalAbility pi, Span<int> result)
{
if (pi is not IPersonalAbility12 a)
return;
result[0] = a.Ability1;
result[1] = a.Ability2;
if (a is not IPersonalAbility12H h)
return;
result[2] = h.AbilityH;
}
public static void SetAbilities(this IPersonalAbility pi, Span<int> result)
{
if (pi is not IPersonalAbility12 a)
return;
a.Ability1 = result[0];
a.Ability2 = result[1];
if (a is not IPersonalAbility12H h)
return;
h.AbilityH = result[2];
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
@ -31,8 +30,9 @@ public abstract class PersonalInfo : IPersonalInfo
public abstract int HatchCycles { get; set; }
public abstract int BaseFriendship { get; set; }
public abstract int EXPGrowth { get; set; }
public abstract IReadOnlyList<int> Abilities { get; set; }
public abstract int GetAbilityIndex(int abilityID);
public abstract int GetIndexOfAbility(int abilityID);
public abstract int GetAbilityAtIndex(int abilityIndex);
public abstract int AbilityCount { get; }
public abstract int EscapeRate { get; set; }
public virtual byte FormCount { get; set; } = 1;
public virtual int FormStatsIndex { get; set; }

View file

@ -190,8 +190,9 @@ public sealed class GP1 : IEncounterInfo, IFixedAbilityNumber
else if (isShiny)
pk.PID ^= 0x1000_0000;
var moves = MoveLevelUp.GetEncounterMoves(pk, Level, GameVersion.GO);
pk.Moves = moves;
Span<ushort> moves = stackalloc ushort[4];
MoveLevelUp.GetEncounterMoves(moves, pk, pk.CurrentLevel, GameVersion.GO);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);
pk.OT_Friendship = pk.PersonalInfo.BaseFriendship;

View file

@ -405,14 +405,14 @@ public partial class PKMEditor
private static int GetAbilityIndex4(PKM pk)
{
var pi = pk.PersonalInfo;
int abilityIndex = pi.GetAbilityIndex(pk.Ability);
int abilityIndex = pi.GetIndexOfAbility(pk.Ability);
if (abilityIndex < 0)
return 0;
if (abilityIndex >= 2)
return 2;
var abils = pi.Abilities;
if (abils[0] == abils[1])
var abils = (IPersonalAbility12)pi;
if (abils.GetIsAbility12Same())
return pk.PIDAbility;
return abilityIndex;
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using PKHeX.Core;
@ -79,27 +78,21 @@ public partial class KChart : Form
cells[c++].Value = p.SPD.ToString("000");
cells[c].Style.BackColor = ColorUtil.ColorBaseStat(p.SPE);
cells[c++].Value = p.SPE.ToString("000");
var abils = p.Abilities;
cells[c++].Value = GetAbility(abils, 0);
cells[c++].Value = GetAbility(abils, 1);
cells[c].Value = GetAbility(abils, 2);
var abils = p.AbilityCount;
cells[c++].Value = abilities[abils > 0 ? p.GetAbilityAtIndex(0) : 0];
cells[c++].Value = abilities[abils > 1 ? p.GetAbilityAtIndex(1) : 0];
cells[c].Value = abilities[abils > 2 ? p.GetAbilityAtIndex(2) : 0];
row.Height = SpriteUtil.Spriter.Height + 1;
DGV.Rows.Add(row);
}
private string GetAbility(IReadOnlyList<int> abilityIDs, int index)
{
if ((uint)index >= abilityIDs.Count)
return abilities[0];
return abilities[abilityIDs[index]];
}
private static bool GetIsNative(IPersonalInfo personalInfo, ushort s) => personalInfo switch
{
PersonalInfo7 => s > 721 || Legal.PastGenAlolanNatives.Contains(s),
PersonalInfo8SWSH ss => ss.IsInDex,
PersonalInfo8BDSP bs => bs.IsInDex,
PersonalInfo8LA bs => bs.IsPresentInGame,
_ => true,
};
}

View file

@ -321,7 +321,7 @@ public partial class SAV_SecretBase : Form
private void SetAbilityList(ushort species, byte form, int abilityIndex)
{
var abilities = PersonalTable.AO.GetFormEntry(species, form).Abilities;
var abilities = PersonalTable.AO.GetFormEntry(species, form);
var list = GameInfo.FilteredSources.GetAbilityList(abilities, 6);
CB_Ability.DataSource = new BindingSource(list, null);
CB_Ability.SelectedIndex = abilityIndex < 3 ? abilityIndex : 0;