mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-22 20:13:06 +00:00
Performance: Slightly reduce allocations in moveset validation (#3460)
* Reuses move parse result objects for each encounter parsed in a LegalityCheck attempt, instead of creating a new object. * Ensures the objects are never-null, and makes cleanup easier. Slightly adjusts some other parts of the moveset validation to reduce allocations.
This commit is contained in:
parent
93255efcb8
commit
69fafcab83
21 changed files with 402 additions and 354 deletions
|
@ -1,6 +1,6 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public enum SlotOrigin
|
||||
public enum SlotOrigin : byte
|
||||
{
|
||||
Party = 0,
|
||||
Box = 1,
|
||||
|
|
|
@ -36,6 +36,5 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
private bool IsDeferredSafari3(bool IsSafariBall) => IsSafariBall != Locations.IsSafariZoneLocation3(Location);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
|
@ -90,20 +90,20 @@ namespace PKHeX.Core
|
|||
if (pkm.Format >= 6)
|
||||
{
|
||||
VerifyRelearnMoves.VerifyRelearn(pkm, info.EncounterOriginal, relearn);
|
||||
if (relearn.Any(z => !z.Valid) && iterator.PeekIsNext())
|
||||
if (!Array.TrueForAll(relearn, z => z.Valid) && iterator.PeekIsNext())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
relearn[i] = VerifyRelearnMoves.DummyValid;
|
||||
foreach (var p in relearn)
|
||||
VerifyRelearnMoves.DummyValid(p);
|
||||
}
|
||||
|
||||
info.Moves = VerifyCurrentMoves.VerifyMoves(pkm, info);
|
||||
if (info.Moves.Any(z => !z.Valid) && iterator.PeekIsNext())
|
||||
VerifyCurrentMoves.VerifyMoves(pkm, info);
|
||||
if (!Array.TrueForAll(info.Moves, z => z.Valid) && iterator.PeekIsNext())
|
||||
return false;
|
||||
|
||||
if (info.Parse.Any(z => !z.Valid) && iterator.PeekIsNext())
|
||||
if (!info.Parse.TrueForAll(z => z.Valid) && iterator.PeekIsNext())
|
||||
return false;
|
||||
|
||||
var evo = EvolutionVerifier.VerifyEvolution(pkm, info);
|
||||
|
@ -128,7 +128,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
else if (pkm is PK1 pk1)
|
||||
{
|
||||
if (info.Moves.Any(z => z.Generation is not 1) && !PK1.IsCatchRateHeldItem(pk1.Catch_Rate))
|
||||
if (!Array.TrueForAll(info.Moves, z => z.Generation is 1) && !PK1.IsCatchRateHeldItem(pk1.Catch_Rate))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ namespace PKHeX.Core
|
|||
|
||||
info.Parse.Add(new CheckResult(Severity.Invalid, hint, CheckIdentifier.Encounter));
|
||||
VerifyRelearnMoves.VerifyRelearn(pkm, info.EncounterOriginal, info.Relearn);
|
||||
info.Moves = VerifyCurrentMoves.VerifyMoves(pkm, info);
|
||||
VerifyCurrentMoves.VerifyMoves(pkm, info);
|
||||
}
|
||||
|
||||
private static string GetHintWhyNotFound(PKM pkm, int gen)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
|
@ -270,7 +269,7 @@ namespace PKHeX.Core
|
|||
return new CheckResult(Severity.Invalid, LG4InvalidTileR45Surf, CheckIdentifier.Encounter);
|
||||
break;
|
||||
case 7:
|
||||
if (s.EggLocation == Locations.Daycare5 && pkm.RelearnMoves.Any(m => m != 0)) // Eevee gift egg
|
||||
if (s.EggLocation == Locations.Daycare5 && pkm.RelearnMove1 != 0) // Eevee gift egg
|
||||
return new CheckResult(Severity.Invalid, LEncStaticRelearn, CheckIdentifier.RelearnMove); // not gift egg
|
||||
break;
|
||||
}
|
||||
|
@ -286,13 +285,14 @@ namespace PKHeX.Core
|
|||
|
||||
private static CheckResult VerifyEncounterTrade(ISpeciesForm pkm, EncounterTrade trade)
|
||||
{
|
||||
if (trade.EvolveOnTrade && trade.Species == pkm.Species)
|
||||
var species = pkm.Species;
|
||||
if (trade.EvolveOnTrade && trade.Species == species)
|
||||
{
|
||||
// Pokemon that evolve on trade can not be in the phase evolution after the trade
|
||||
// If the trade holds an everstone EvolveOnTrade will be false for the encounter
|
||||
var species = ParseSettings.SpeciesStrings;
|
||||
var unevolved = species[pkm.Species];
|
||||
var evolved = species[pkm.Species + 1];
|
||||
var names = ParseSettings.SpeciesStrings;
|
||||
var evolved = names[species + 1];
|
||||
var unevolved = names[species];
|
||||
return new CheckResult(Severity.Invalid, string.Format(LEvoTradeReq, unevolved, evolved), CheckIdentifier.Encounter);
|
||||
}
|
||||
return new CheckResult(Severity.Valid, LEncTradeMatch, CheckIdentifier.Encounter);
|
||||
|
|
|
@ -21,51 +21,53 @@ namespace PKHeX.Core
|
|||
/// <param name="pkm">Data to check</param>
|
||||
/// <param name="info">Encounter conditions and legality info</param>
|
||||
/// <returns>Validity of the <see cref="PKM.Moves"/></returns>
|
||||
public static CheckMoveResult[] VerifyMoves(PKM pkm, LegalInfo info)
|
||||
public static void VerifyMoves(PKM pkm, LegalInfo info)
|
||||
{
|
||||
var parse = info.Moves;
|
||||
Array.ForEach(parse, p => p.Reset());
|
||||
var currentMoves = pkm.Moves;
|
||||
var res = ParseMovesForEncounters(pkm, info, currentMoves);
|
||||
ParseMovesForEncounters(pkm, parse, info, currentMoves);
|
||||
|
||||
// Duplicate Moves Check
|
||||
VerifyNoEmptyDuplicates(currentMoves, res);
|
||||
VerifyNoEmptyDuplicates(currentMoves, parse);
|
||||
if (currentMoves[0] == 0) // Can't have an empty move slot for the first move.
|
||||
res[0] = new CheckMoveResult(res[0], Invalid, LMoveSourceEmpty, CurrentMove);
|
||||
|
||||
return res;
|
||||
parse[0].FlagIllegal(LMoveSourceEmpty, CurrentMove);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesForEncounters(PKM pkm, LegalInfo info, int[] currentMoves)
|
||||
private static void ParseMovesForEncounters(PKM pkm, CheckMoveResult[] parse, LegalInfo info, int[] currentMoves)
|
||||
{
|
||||
if (pkm.Species == (int)Species.Smeargle) // special handling for Smeargle
|
||||
return ParseMovesForSmeargle(pkm, currentMoves, info); // Smeargle can have any moves except a few
|
||||
{
|
||||
ParseMovesForSmeargle(pkm, parse, currentMoves, info); // Smeargle can have any moves except a few
|
||||
return;
|
||||
}
|
||||
|
||||
// gather valid moves for encounter species
|
||||
info.EncounterMoves = new ValidEncounterMoves(pkm, info.EncounterMatch, info.EvoChainsAllGens);
|
||||
|
||||
var res = info.Generation < 6
|
||||
? ParseMovesPre3DS(pkm, currentMoves, info)
|
||||
: ParseMoves3DS(pkm, currentMoves, info);
|
||||
|
||||
if (res.All(x => x.Valid))
|
||||
return res;
|
||||
|
||||
return res;
|
||||
if (info.Generation >= 6)
|
||||
ParseMoves3DS(pkm, parse, currentMoves, info);
|
||||
else
|
||||
ParseMovesPre3DS(pkm, parse, currentMoves, info);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesForSmeargle(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info)
|
||||
private static void ParseMovesForSmeargle(PKM pkm, CheckMoveResult[] parse, int[] currentMoves, LegalInfo info)
|
||||
{
|
||||
if (!pkm.IsEgg)
|
||||
return ParseMovesSketch(pkm, currentMoves);
|
||||
{
|
||||
ParseMovesSketch(pkm, parse, currentMoves);
|
||||
return;
|
||||
}
|
||||
|
||||
// can only know sketch as egg
|
||||
var levelup = new int[info.EvoChainsAllGens.Length][];
|
||||
levelup[pkm.Format] = new[] {166};
|
||||
levelup[pkm.Format] = new[] { (int)Move.Sketch };
|
||||
info.EncounterMoves = new ValidEncounterMoves(levelup);
|
||||
var source = new MoveParseSource { CurrentMoves = currentMoves };
|
||||
return ParseMoves(pkm, source, info);
|
||||
ParseMoves(pkm, source, info, parse);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesWasEggPreRelearn(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info, EncounterEgg e)
|
||||
private static void ParseMovesWasEggPreRelearn(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves, LegalInfo info, EncounterEgg e)
|
||||
{
|
||||
// Level up moves could not be inherited if Ditto is parent,
|
||||
// that means genderless species and male only species (except Nidoran-M and Volbeat; they breed with Nidoran-F and Illumise) could not have level up moves as an egg
|
||||
|
@ -94,51 +96,54 @@ namespace PKHeX.Core
|
|||
EggLevelUpSource = LevelUp,
|
||||
EggMoveSource = Egg,
|
||||
};
|
||||
return ParseMoves(pkm, source, info);
|
||||
ParseMoves(pkm, source, info, parse);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesSketch(PKM pkm, IReadOnlyList<int> currentMoves)
|
||||
private static void ParseMovesSketch(PKM pkm, CheckMoveResult[] parse, ReadOnlySpan<int> currentMoves)
|
||||
{
|
||||
var res = new CheckMoveResult[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = parse.Length - 1; i >= 0; i--)
|
||||
{
|
||||
var r = parse[i];
|
||||
var move = currentMoves[i];
|
||||
res[i] = Legal.IsValidSketch(move, pkm.Format)
|
||||
? new CheckMoveResult(Sketch, pkm.Format, CurrentMove)
|
||||
: new CheckMoveResult(Unknown, pkm.Format, Invalid, LMoveSourceInvalidSketch, CurrentMove);
|
||||
if (move == 0)
|
||||
r.Set(None, pkm.Format, Valid, LMoveSourceEmpty, CurrentMove);
|
||||
else if (Legal.IsValidSketch(move, pkm.Format))
|
||||
r.Set(Sketch, pkm.Format, Valid, L_AValid, CurrentMove);
|
||||
else
|
||||
r.Set(Unknown, pkm.Format, Invalid, LMoveSourceInvalidSketch, CurrentMove);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMoves3DS(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info)
|
||||
private static void ParseMoves3DS(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves, LegalInfo info)
|
||||
{
|
||||
info.EncounterMoves.Relearn = info.Generation >= 6 ? pkm.RelearnMoves : Array.Empty<int>();
|
||||
return info.EncounterMatch is IMoveset
|
||||
? ParseMovesSpecialMoveset(pkm, currentMoves, info)
|
||||
: ParseMovesRelearn(pkm, currentMoves, info);
|
||||
if (info.EncounterMatch is IMoveset)
|
||||
ParseMovesSpecialMoveset(pkm, currentMoves, info, parse);
|
||||
else
|
||||
ParseMovesRelearn(pkm, currentMoves, info, parse);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesPre3DS(PKM pkm, int[] currentMoves, LegalInfo info)
|
||||
private static void ParseMovesPre3DS(PKM pkm, CheckMoveResult[] parse, int[] currentMoves, LegalInfo info)
|
||||
{
|
||||
if (info.EncounterMatch is EncounterEgg e)
|
||||
{
|
||||
return pkm.IsEgg
|
||||
? VerifyPreRelearnEggBase(currentMoves, e)
|
||||
: ParseMovesWasEggPreRelearn(pkm, currentMoves, info, e);
|
||||
if (pkm.IsEgg)
|
||||
VerifyRelearnMoves.VerifyEggMoveset(e, parse, currentMoves, CurrentMove);
|
||||
else
|
||||
ParseMovesWasEggPreRelearn(pkm, parse, currentMoves, info, e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Not all games have a re-learner. Initial moves may not fill out all 4 slots.
|
||||
int gen = info.EncounterMatch.Generation;
|
||||
if (gen == 1 || (gen == 2 && !AllowGen2MoveReminder(pkm)))
|
||||
return ParseMovesGenGB(pkm, currentMoves, info);
|
||||
ParseMovesGenGB(pkm, currentMoves, info, parse);
|
||||
|
||||
return ParseMovesSpecialMoveset(pkm, currentMoves, info);
|
||||
ParseMovesSpecialMoveset(pkm, currentMoves, info, parse);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesGenGB(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info)
|
||||
private static void ParseMovesGenGB(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info, CheckMoveResult[] parse)
|
||||
{
|
||||
var res = new CheckMoveResult[4];
|
||||
var enc = info.EncounterMatch;
|
||||
var evos = info.EvoChainsAllGens[enc.Generation];
|
||||
var level = evos.Count > 0 ? evos[^1].MinLevel : enc.LevelMin;
|
||||
|
@ -149,7 +154,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
var VerInitialMoves = enc is IMoveset {Moves.Count: not 0 } x ? (int[])x.Moves : MoveLevelUp.GetEncounterMoves(enc.Species, 0, level, ver);
|
||||
if (VerInitialMoves.Intersect(InitialMoves).Count() == VerInitialMoves.Length)
|
||||
return res;
|
||||
return;
|
||||
|
||||
var source = new MoveParseSource
|
||||
{
|
||||
|
@ -157,14 +162,14 @@ namespace PKHeX.Core
|
|||
SpecialSource = SpecialMoves,
|
||||
Base = VerInitialMoves,
|
||||
};
|
||||
res = ParseMoves(pkm, source, info);
|
||||
ParseMoves(pkm, source, info, parse);
|
||||
|
||||
// Must have a minimum count of moves, depending on the tradeback state.
|
||||
if (pkm is PK1 pk1)
|
||||
{
|
||||
int count = GBRestrictions.GetRequiredMoveCount(pk1, source.CurrentMoves, info, source.Base);
|
||||
if (count == 1)
|
||||
return res;
|
||||
return;
|
||||
|
||||
// Reverse for loop and break instead of 0..count continue -- early-breaks for the vast majority of cases.
|
||||
// We already flag for empty interstitial moveslots.
|
||||
|
@ -179,24 +184,23 @@ namespace PKHeX.Core
|
|||
// Evolution canceling also leads to incorrect assumptions in the above used method, so just indicate them as fishy in that case.
|
||||
// Not leveled up? Not possible to be missing the move slot.
|
||||
var severity = enc.LevelMin == pkm.CurrentLevel ? Invalid : Fishy;
|
||||
res[m] = new CheckMoveResult(None, pkm.Format, severity, LMoveSourceEmpty, CurrentMove);
|
||||
parse[m].Set(None, pkm.Format, severity, LMoveSourceEmpty, CurrentMove);
|
||||
}
|
||||
}
|
||||
if (res.All(r => r.Valid))
|
||||
return res;
|
||||
if (Array.TrueForAll(parse, z => z.Valid))
|
||||
return;
|
||||
InitialMoves = VerInitialMoves;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesSpecialMoveset(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info)
|
||||
private static void ParseMovesSpecialMoveset(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info, CheckMoveResult[] parse)
|
||||
{
|
||||
var source = new MoveParseSource
|
||||
{
|
||||
CurrentMoves = currentMoves,
|
||||
SpecialSource = GetSpecialMoves(info.EncounterMatch),
|
||||
};
|
||||
return ParseMoves(pkm, source, info);
|
||||
ParseMoves(pkm, source, info, parse);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<int> GetSpecialMoves(IEncounterTemplate enc)
|
||||
|
@ -206,7 +210,7 @@ namespace PKHeX.Core
|
|||
return Array.Empty<int>();
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMovesRelearn(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info)
|
||||
private static void ParseMovesRelearn(PKM pkm, IReadOnlyList<int> currentMoves, LegalInfo info, CheckMoveResult[] parse)
|
||||
{
|
||||
var source = new MoveParseSource
|
||||
{
|
||||
|
@ -217,22 +221,22 @@ namespace PKHeX.Core
|
|||
if (info.EncounterMatch is EncounterEgg e)
|
||||
source.EggMoveSource = MoveEgg.GetEggMoves(pkm.PersonalInfo, e.Species, e.Form, e.Version, e.Generation);
|
||||
|
||||
var res = ParseMoves(pkm, source, info);
|
||||
ParseMoves(pkm, source, info, parse);
|
||||
var relearn = pkm.RelearnMoves;
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = parse.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if ((pkm.IsEgg || res[i].IsRelearn) && !relearn.Contains(currentMoves[i]))
|
||||
res[i] = new CheckMoveResult(res[i], Invalid, string.Format(LMoveRelearnFMiss_0, res[i].Comment), res[i].Identifier);
|
||||
}
|
||||
var r = parse[i];
|
||||
if (!r.IsRelearn && !pkm.IsEgg)
|
||||
continue;
|
||||
if (relearn.Contains(currentMoves[i]))
|
||||
continue;
|
||||
|
||||
return res;
|
||||
r.FlagIllegal(string.Format(LMoveRelearnFMiss_0, r.Comment));
|
||||
}
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] ParseMoves(PKM pkm, MoveParseSource source, LegalInfo info)
|
||||
private static void ParseMoves(PKM pkm, MoveParseSource source, LegalInfo info, CheckMoveResult[] parse)
|
||||
{
|
||||
var res = new CheckMoveResult[4];
|
||||
bool AllParsed() => res.All(z => z != null);
|
||||
|
||||
// Special considerations!
|
||||
const int NoMinGeneration = 0;
|
||||
int minGeneration = NoMinGeneration;
|
||||
|
@ -243,17 +247,18 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
// Check empty moves and relearn moves before generation specific moves
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
var move = source.CurrentMoves[m];
|
||||
var r = parse[m];
|
||||
if (move == 0)
|
||||
res[m] = new CheckMoveResult(None, pkm.Format, Valid, LMoveSourceEmpty, CurrentMove);
|
||||
r.Set(None, pkm.Format, Valid, LMoveSourceEmpty, CurrentMove);
|
||||
else if (minGeneration == NoMinGeneration && info.EncounterMoves.Relearn.Contains(move))
|
||||
res[m] = new CheckMoveResult(Relearn, info.Generation, Valid, LMoveSourceRelearn, CurrentMove);
|
||||
r.Set(Relearn, info.Generation, Valid, LMoveSourceRelearn, CurrentMove);
|
||||
}
|
||||
|
||||
if (AllParsed())
|
||||
return res;
|
||||
if (Array.TrueForAll(parse, z => z.IsParsed))
|
||||
return;
|
||||
|
||||
// Encapsulate arguments to simplify method calls
|
||||
var moveInfo = new LearnInfo(pkm, source);
|
||||
|
@ -269,49 +274,52 @@ namespace PKHeX.Core
|
|||
int lastgen = generations[^1];
|
||||
foreach (var gen in generations)
|
||||
{
|
||||
ParseMovesByGeneration(pkm, res, gen, info, moveInfo, lastgen);
|
||||
if (AllParsed())
|
||||
return res;
|
||||
ParseMovesByGeneration(pkm, parse, gen, info, moveInfo, lastgen);
|
||||
if (Array.TrueForAll(parse, z => z.IsParsed))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (pkm.Species == (int)Species.Shedinja && info.Generation <= 4)
|
||||
ParseShedinjaEvolveMoves(pkm, res, source.CurrentMoves, info.EvoChainsAllGens);
|
||||
ParseShedinjaEvolveMoves(pkm, parse, source.CurrentMoves, info.EvoChainsAllGens);
|
||||
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
for (int m = 0; m < 4; m++)
|
||||
res[m] ??= new CheckMoveResult(Unknown, info.Generation, Invalid, LMoveSourceInvalid, CurrentMove);
|
||||
return res;
|
||||
foreach (var r in parse)
|
||||
{
|
||||
if (!r.IsParsed)
|
||||
r.Set(Unknown, info.Generation, Invalid, LMoveSourceInvalid, CurrentMove);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseMovesByGeneration(PKM pkm, CheckMoveResult[] res, int gen, LegalInfo info, LearnInfo learnInfo, int last)
|
||||
private static void ParseMovesByGeneration(PKM pkm, CheckMoveResult[] parse, int gen, LegalInfo info, LearnInfo learnInfo, int last)
|
||||
{
|
||||
GetHMCompatibility(pkm, res, gen, learnInfo.Source.CurrentMoves, out bool[] HMLearned, out bool KnowDefogWhirlpool);
|
||||
ParseMovesByGeneration(pkm, res, gen, info, learnInfo);
|
||||
Span<bool> HMLearned = stackalloc bool[parse.Length];
|
||||
bool KnowDefogWhirlpool = GetHMCompatibility(pkm, parse, gen, learnInfo.Source.CurrentMoves, HMLearned);
|
||||
ParseMovesByGeneration(pkm, parse, gen, info, learnInfo);
|
||||
|
||||
if (gen == last)
|
||||
ParseMovesByGenerationLast(pkm, res, learnInfo, info.EncounterMatch);
|
||||
ParseMovesByGenerationLast(parse, learnInfo, info.EncounterMatch);
|
||||
|
||||
switch (gen)
|
||||
{
|
||||
case 1 or 2:
|
||||
ParseMovesByGeneration12(pkm, res, learnInfo.Source.CurrentMoves, gen, info, learnInfo);
|
||||
ParseMovesByGeneration12(pkm, parse, learnInfo.Source.CurrentMoves, gen, info, learnInfo);
|
||||
break;
|
||||
|
||||
case 3 or 4:
|
||||
if (pkm.Format > gen)
|
||||
FlagIncompatibleTransferHMs45(res, learnInfo.Source.CurrentMoves, gen, HMLearned, KnowDefogWhirlpool);
|
||||
FlagIncompatibleTransferHMs45(parse, learnInfo.Source.CurrentMoves, gen, HMLearned, KnowDefogWhirlpool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseMovesByGeneration(PKM pkm, IList<CheckMoveResult> res, int gen, LegalInfo info, LearnInfo learnInfo)
|
||||
private static void ParseMovesByGeneration(PKM pkm, CheckMoveResult[] parse, int gen, LegalInfo info, LearnInfo learnInfo)
|
||||
{
|
||||
var moves = learnInfo.Source.CurrentMoves;
|
||||
bool native = gen == pkm.Format;
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
if (IsCheckValid(res[m])) // already validated with another generation
|
||||
var r = parse[m];
|
||||
if (IsCheckValid(r)) // already validated with another generation
|
||||
continue;
|
||||
int move = moves[m];
|
||||
if (move == 0)
|
||||
|
@ -321,26 +329,26 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (gen == 2 && !native && move > Legal.MaxMoveID_1 && pkm.VC1)
|
||||
{
|
||||
res[m] = new CheckMoveResult(Unknown, gen, Invalid, LMoveSourceInvalid, CurrentMove);
|
||||
r.Set(Unknown, gen, Invalid, LMoveSourceInvalid, CurrentMove);
|
||||
continue;
|
||||
}
|
||||
if (gen == 2 && learnInfo.Source.EggMoveSource.Contains(move))
|
||||
res[m] = new CheckMoveResult(EggMove, gen, Valid, LMoveSourceEgg, CurrentMove);
|
||||
r.Set(EggMove, gen, Valid, LMoveSourceEgg, CurrentMove);
|
||||
else if (learnInfo.Source.Base.Contains(move))
|
||||
res[m] = new CheckMoveResult(Initial, gen, Valid, native ? LMoveSourceDefault : string.Format(LMoveFDefault_0, gen), CurrentMove);
|
||||
r.Set(Initial, gen, Valid, native ? LMoveSourceDefault : string.Format(LMoveFDefault_0, gen), CurrentMove);
|
||||
}
|
||||
if (info.EncounterMoves.LevelUpMoves[gen].Contains(move))
|
||||
res[m] = new CheckMoveResult(LevelUp, gen, Valid, native ? LMoveSourceLevelUp : string.Format(LMoveFLevelUp_0, gen), CurrentMove);
|
||||
r.Set(LevelUp, gen, Valid, native ? LMoveSourceLevelUp : string.Format(LMoveFLevelUp_0, gen), CurrentMove);
|
||||
else if (info.EncounterMoves.TMHMMoves[gen].Contains(move))
|
||||
res[m] = new CheckMoveResult(TMHM, gen, Valid, native ? LMoveSourceTMHM : string.Format(LMoveFTMHM_0, gen), CurrentMove);
|
||||
r.Set(TMHM, gen, Valid, native ? LMoveSourceTMHM : string.Format(LMoveFTMHM_0, gen), CurrentMove);
|
||||
else if (info.EncounterMoves.TutorMoves[gen].Contains(move))
|
||||
res[m] = new CheckMoveResult(Tutor, gen, Valid, native ? LMoveSourceTutor : string.Format(LMoveFTutor_0, gen), CurrentMove);
|
||||
r.Set(Tutor, gen, Valid, native ? LMoveSourceTutor : string.Format(LMoveFTutor_0, gen), CurrentMove);
|
||||
else if (gen == info.Generation && learnInfo.Source.SpecialSource.Contains(move))
|
||||
res[m] = new CheckMoveResult(Special, gen, Valid, LMoveSourceSpecial, CurrentMove);
|
||||
r.Set(Special, gen, Valid, LMoveSourceSpecial, CurrentMove);
|
||||
else if (gen >= 8 && MoveEgg.GetIsSharedEggMove(pkm, gen, move))
|
||||
res[m] = new CheckMoveResult(Shared, gen, Valid, native ? LMoveSourceShared : string.Format(LMoveSourceSharedF, gen), CurrentMove);
|
||||
r.Set(Shared, gen, Valid, native ? LMoveSourceShared : string.Format(LMoveSourceSharedF, gen), CurrentMove);
|
||||
|
||||
if (gen >= 3 || !IsCheckValid(res[m]))
|
||||
if (gen >= 3 || !IsCheckValid(r))
|
||||
continue;
|
||||
|
||||
// Gen1/Gen2 only below
|
||||
|
@ -357,41 +365,41 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
private static void ParseMovesByGeneration12(PKM pkm, CheckMoveResult[] res, IReadOnlyList<int> currentMoves, int gen, LegalInfo info, LearnInfo learnInfo)
|
||||
private static void ParseMovesByGeneration12(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves, int gen, LegalInfo info, LearnInfo learnInfo)
|
||||
{
|
||||
// Mark the gen 1 exclusive moves as illegal because the pokemon also have Non tradeback egg moves.
|
||||
if (learnInfo.MixedGen12NonTradeback)
|
||||
{
|
||||
foreach (int m in learnInfo.Gen1Moves)
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LG1MoveExclusive, CurrentMove);
|
||||
parse[m].FlagIllegal(LG1MoveExclusive, CurrentMove);
|
||||
|
||||
foreach (int m in learnInfo.Gen2PreevoMoves)
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LG1TradebackPreEvoMove, CurrentMove);
|
||||
parse[m].FlagIllegal(LG1TradebackPreEvoMove, CurrentMove);
|
||||
}
|
||||
|
||||
if (gen == 1 && pkm.Format == 1 && !AllowGen1Tradeback)
|
||||
{
|
||||
ParseRedYellowIncompatibleMoves(pkm, res, currentMoves);
|
||||
ParseEvolutionsIncompatibleMoves(pkm, res, currentMoves, info.EncounterMoves.TMHMMoves[1]);
|
||||
ParseRedYellowIncompatibleMoves(pkm, parse, currentMoves);
|
||||
ParseEvolutionsIncompatibleMoves(pkm, parse, currentMoves, info.EncounterMoves.TMHMMoves[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseMovesByGenerationLast(PKM pkm, CheckMoveResult[] res, LearnInfo learnInfo, IEncounterTemplate enc)
|
||||
private static void ParseMovesByGenerationLast(CheckMoveResult[] parse, LearnInfo learnInfo, IEncounterTemplate enc)
|
||||
{
|
||||
int gen = enc.Generation;
|
||||
ParseEggMovesInherited(pkm, res, gen, learnInfo);
|
||||
ParseEggMoves(pkm, res, gen, learnInfo);
|
||||
ParseEggMovesRemaining(pkm, res, learnInfo, enc);
|
||||
ParseEggMovesInherited(parse, gen, learnInfo);
|
||||
ParseEggMoves(parse, gen, learnInfo);
|
||||
ParseEggMovesRemaining(parse, learnInfo, enc);
|
||||
}
|
||||
|
||||
private static void ParseEggMovesInherited(PKM pkm, CheckMoveResult[] res, int gen, LearnInfo learnInfo)
|
||||
private static void ParseEggMovesInherited(CheckMoveResult[] parse, int gen, LearnInfo learnInfo)
|
||||
{
|
||||
var moves = learnInfo.Source.CurrentMoves;
|
||||
// Check higher-level moves after all the moves but just before egg moves to differentiate it from normal level up moves
|
||||
// Also check if the base egg moves is a non tradeback move
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
var r = res[m];
|
||||
var r = parse[m];
|
||||
if (IsCheckValid(r)) // already validated
|
||||
{
|
||||
if (gen == 2 && r.Generation != 1)
|
||||
|
@ -406,12 +414,12 @@ namespace PKHeX.Core
|
|||
|
||||
if (learnInfo.IsGen2Pkm && learnInfo.Gen1Moves.Count != 0 && move > Legal.MaxMoveID_1)
|
||||
{
|
||||
res[m] = new CheckMoveResult(InheritLevelUp, gen, Invalid, LG1MoveTradeback, CurrentMove);
|
||||
r.Set(InheritLevelUp, gen, Invalid, LG1MoveTradeback, CurrentMove);
|
||||
learnInfo.MixedGen12NonTradeback = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
res[m] = new CheckMoveResult(InheritLevelUp, gen, Valid, LMoveEggLevelUp, CurrentMove);
|
||||
r.Set(InheritLevelUp, gen, Valid, LMoveEggLevelUp, CurrentMove);
|
||||
}
|
||||
learnInfo.LevelUpEggMoves.Add(m);
|
||||
if (gen == 2 && learnInfo.Gen1Moves.Contains(m))
|
||||
|
@ -419,14 +427,15 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
private static void ParseEggMoves(PKM pkm, CheckMoveResult[] res, int gen, LearnInfo learnInfo)
|
||||
private static void ParseEggMoves(CheckMoveResult[] parse, int gen, LearnInfo learnInfo)
|
||||
{
|
||||
var moves = learnInfo.Source.CurrentMoves;
|
||||
// Check egg moves after all the generations and all the moves, every move that can't be learned in another source should have preference
|
||||
// the moves that can only be learned from egg moves should in the future check if the move combinations can be breed in gens 2 to 5
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
if (IsCheckValid(res[m]))
|
||||
var r = parse[m];
|
||||
if (IsCheckValid(r))
|
||||
continue;
|
||||
int move = moves[m];
|
||||
if (move == 0)
|
||||
|
@ -439,12 +448,12 @@ namespace PKHeX.Core
|
|||
// without removing moves above MaxMoveID_1, egg moves above MaxMoveID_1 and gen 1 moves are incompatible
|
||||
if (learnInfo.IsGen2Pkm && learnInfo.Gen1Moves.Count != 0 && move > Legal.MaxMoveID_1)
|
||||
{
|
||||
res[m] = new CheckMoveResult(EggMove, gen, Invalid, LG1MoveTradeback, CurrentMove);
|
||||
r.Set(EggMove, gen, Invalid, LG1MoveTradeback, CurrentMove);
|
||||
learnInfo.MixedGen12NonTradeback = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
res[m] = new CheckMoveResult(EggMove, gen, Valid, LMoveSourceEgg, CurrentMove);
|
||||
r.Set(EggMove, gen, Valid, LMoveSourceEgg, CurrentMove);
|
||||
}
|
||||
|
||||
learnInfo.EggMovesLearned.Add(m);
|
||||
|
@ -456,19 +465,19 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (learnInfo.IsGen2Pkm && learnInfo.Gen1Moves.Count != 0 && move > Legal.MaxMoveID_1)
|
||||
{
|
||||
res[m] = new CheckMoveResult(SpecialEgg, gen, Invalid, LG1MoveTradeback, CurrentMove);
|
||||
r.Set(SpecialEgg, gen, Invalid, LG1MoveTradeback, CurrentMove);
|
||||
learnInfo.MixedGen12NonTradeback = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
res[m] = new CheckMoveResult(SpecialEgg, gen, Valid, LMoveSourceEggEvent, CurrentMove);
|
||||
r.Set(SpecialEgg, gen, Valid, LMoveSourceEggEvent, CurrentMove);
|
||||
}
|
||||
}
|
||||
learnInfo.EventEggMoves.Add(m);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseEggMovesRemaining(PKM pkm, CheckMoveResult[] res, LearnInfo learnInfo, IEncounterTemplate enc)
|
||||
private static void ParseEggMovesRemaining(CheckMoveResult[] parse, LearnInfo learnInfo, IEncounterTemplate enc)
|
||||
{
|
||||
// A pokemon could have normal egg moves and regular egg moves
|
||||
// Only if all regular egg moves are event egg moves or all event egg moves are regular egg moves
|
||||
|
@ -483,14 +492,14 @@ namespace PKHeX.Core
|
|||
if (isEvent)
|
||||
{
|
||||
if (!learnInfo.EggMovesLearned.Contains(m))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LMoveEggIncompatibleEvent, CurrentMove);
|
||||
parse[m].FlagIllegal(LMoveEggIncompatibleEvent, CurrentMove);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (learnInfo.EggMovesLearned.Contains(m))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LMoveEggIncompatible, CurrentMove);
|
||||
parse[m].FlagIllegal(LMoveEggIncompatible, CurrentMove);
|
||||
else if (learnInfo.LevelUpEggMoves.Contains(m))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LMoveEventEggLevelUp, CurrentMove);
|
||||
parse[m].FlagIllegal(LMoveEventEggLevelUp, CurrentMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,22 +510,22 @@ namespace PKHeX.Core
|
|||
foreach (int m in RegularEggMovesLearned)
|
||||
{
|
||||
if (learnInfo.EggMovesLearned.Contains(m))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, gift ? LMoveEggMoveGift : LMoveEggInvalidEvent, CurrentMove);
|
||||
parse[m].FlagIllegal(gift ? LMoveEggMoveGift : LMoveEggInvalidEvent, CurrentMove);
|
||||
else if (learnInfo.LevelUpEggMoves.Contains(m))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, gift ? LMoveEggInvalidEventLevelUpGift : LMoveEggInvalidEventLevelUp, CurrentMove);
|
||||
parse[m].FlagIllegal(gift ? LMoveEggInvalidEventLevelUpGift : LMoveEggInvalidEventLevelUp, CurrentMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseRedYellowIncompatibleMoves(PKM pkm, IList<CheckMoveResult> res, IReadOnlyList<int> currentMoves)
|
||||
private static void ParseRedYellowIncompatibleMoves(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves)
|
||||
{
|
||||
var incompatible = GetIncompatibleRBYMoves(pkm, currentMoves);
|
||||
if (incompatible.Count == 0)
|
||||
return;
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
if (incompatible.Contains(currentMoves[m]))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LG1MoveLearnSameLevel, CurrentMove);
|
||||
parse[m].FlagIllegal(LG1MoveLearnSameLevel, CurrentMove);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,12 +541,12 @@ namespace PKHeX.Core
|
|||
case (int)Species.Vaporeon when pkm.CurrentLevel < 47 && currentMoves.Contains((int)Move.AcidArmor):
|
||||
{
|
||||
var incompatible = new List<int>(3);
|
||||
if (currentMoves.Contains((int)Move.Mist))
|
||||
incompatible.Add((int)Move.Mist);
|
||||
if (currentMoves.Contains((int)Move.Haze))
|
||||
incompatible.Add((int)Move.Haze);
|
||||
if (currentMoves.Contains((int)Move.Mist)) incompatible.Add((int)Move.Mist);
|
||||
if (currentMoves.Contains((int)Move.Haze)) incompatible.Add((int)Move.Haze);
|
||||
if (incompatible.Count != 0)
|
||||
incompatible.Add((int)Move.AcidArmor);
|
||||
else
|
||||
return Array.Empty<int>();
|
||||
return incompatible;
|
||||
}
|
||||
|
||||
|
@ -550,7 +559,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
private static void ParseEvolutionsIncompatibleMoves(PKM pkm, IList<CheckMoveResult> res, IReadOnlyList<int> moves, IReadOnlyList<int> tmhm)
|
||||
private static void ParseEvolutionsIncompatibleMoves(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> moves, IReadOnlyList<int> tmhm)
|
||||
{
|
||||
GBRestrictions.GetIncompatibleEvolutionMoves(pkm, moves, tmhm,
|
||||
out var prevSpeciesID,
|
||||
|
@ -562,18 +571,18 @@ namespace PKHeX.Core
|
|||
|
||||
var prev = SpeciesStrings[prevSpeciesID];
|
||||
var curr = SpeciesStrings[pkm.Species];
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
if (incompatCurr.Contains(moves[m]))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, string.Format(LMoveEvoFLower, curr, prev), CurrentMove);
|
||||
parse[m].FlagIllegal(string.Format(LMoveEvoFLower, curr, prev), CurrentMove);
|
||||
if (incompatPrev.Contains(moves[m]))
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, string.Format(LMoveEvoFHigher, curr, prev), CurrentMove);
|
||||
parse[m].FlagIllegal(string.Format(LMoveEvoFHigher, curr, prev), CurrentMove);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseShedinjaEvolveMoves(PKM pkm, IList<CheckMoveResult> res, IReadOnlyList<int> currentMoves, IReadOnlyList<IReadOnlyList<EvoCriteria>> evos)
|
||||
private static void ParseShedinjaEvolveMoves(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves, IReadOnlyList<IReadOnlyList<EvoCriteria>> evos)
|
||||
{
|
||||
var ShedinjaEvoMovesLearned = new List<int>();
|
||||
int shedinjaEvoMoveIndex = 0;
|
||||
var format = pkm.Format;
|
||||
for (int gen = Math.Min(format, 4); gen >= 3; gen--)
|
||||
{
|
||||
|
@ -585,77 +594,88 @@ namespace PKHeX.Core
|
|||
var maxLevel = pkm.CurrentLevel;
|
||||
var ninjaskMoves = MoveList.GetShedinjaEvolveMoves(pkm, gen, maxLevel);
|
||||
bool native = gen == format;
|
||||
for (int m = 0; m < 4; m++)
|
||||
for (int m = parse.Length - 1; m >= 0; m--)
|
||||
{
|
||||
if (IsCheckValid(res[m])) // already validated
|
||||
var r = parse[m];
|
||||
if (IsCheckValid(r)) // already validated
|
||||
continue;
|
||||
|
||||
if (!ninjaskMoves.Contains(currentMoves[m]))
|
||||
continue;
|
||||
|
||||
// Can't have more than one Ninjask exclusive move on Shedinja
|
||||
if (shedinjaEvoMoveIndex != 0)
|
||||
{
|
||||
r.FlagIllegal(LMoveNincada, CurrentMove);
|
||||
continue;
|
||||
}
|
||||
|
||||
var msg = native ? LMoveNincadaEvo : string.Format(LMoveNincadaEvoF_0, gen);
|
||||
res[m] = new CheckMoveResult(ShedinjaEvo, gen, Valid, msg, CurrentMove);
|
||||
ShedinjaEvoMovesLearned.Add(m);
|
||||
r.Set(ShedinjaEvo, gen, Valid, msg, CurrentMove);
|
||||
shedinjaEvoMoveIndex = m;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShedinjaEvoMovesLearned.Count == 0)
|
||||
if (shedinjaEvoMoveIndex == 0)
|
||||
return;
|
||||
if (ShedinjaEvoMovesLearned.Count > 1)
|
||||
{
|
||||
// Can't have more than one Ninjask exclusive move on Shedinja
|
||||
foreach (int m in ShedinjaEvoMovesLearned)
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, LMoveNincada, CurrentMove);
|
||||
return;
|
||||
}
|
||||
|
||||
// Double check that the Ninjask move level isn't less than any Nincada move level
|
||||
int move = ShedinjaEvoMovesLearned[0];
|
||||
int g = res[move].Generation;
|
||||
int levelJ = MoveList.GetShedinjaMoveLevel((int)Species.Ninjask, currentMoves[move], g);
|
||||
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (m != move)
|
||||
continue;
|
||||
if (res[m].Source != LevelUp)
|
||||
continue;
|
||||
int levelS = MoveList.GetShedinjaMoveLevel((int)Species.Shedinja, currentMoves[m], res[m].Generation);
|
||||
if (levelS > 0)
|
||||
continue;
|
||||
var r = parse[shedinjaEvoMoveIndex];
|
||||
if (r.Source != LevelUp)
|
||||
return;
|
||||
|
||||
int levelN = MoveList.GetShedinjaMoveLevel((int)Species.Nincada, currentMoves[m], res[m].Generation);
|
||||
var move = currentMoves[shedinjaEvoMoveIndex];
|
||||
int levelS = MoveList.GetShedinjaMoveLevel((int)Species.Shedinja, move, r.Generation);
|
||||
if (levelS > 0)
|
||||
return;
|
||||
|
||||
int levelN = MoveList.GetShedinjaMoveLevel((int)Species.Nincada, move, r.Generation);
|
||||
int levelJ = MoveList.GetShedinjaMoveLevel((int)Species.Ninjask, move, r.Generation);
|
||||
if (levelN > levelJ)
|
||||
res[m] = new CheckMoveResult(res[m], Invalid, string.Format(LMoveEvoFHigher, SpeciesStrings[(int)Species.Nincada], SpeciesStrings[(int)Species.Ninjask]), CurrentMove);
|
||||
r.FlagIllegal(string.Format(LMoveEvoFHigher, SpeciesStrings[(int)Species.Nincada], SpeciesStrings[(int)Species.Ninjask]), CurrentMove);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetHMCompatibility(PKM pkm, IReadOnlyList<CheckResult> res, int gen, IReadOnlyList<int> moves, out bool[] HMLearned, out bool KnowDefogWhirlpool)
|
||||
private static bool GetHMCompatibility(PKM pkm, IReadOnlyList<CheckMoveResult> parse, int gen, IReadOnlyList<int> moves, Span<bool> HMLearned)
|
||||
{
|
||||
HMLearned = new bool[4];
|
||||
// Check if pokemon knows HM moves from generation 3 and 4 but are not valid yet, that means it cant learn the HMs in future generations
|
||||
if (gen == 4 && pkm.Format > 4)
|
||||
{
|
||||
IsHMSource(HMLearned, Legal.HM_4_RemovePokeTransfer);
|
||||
KnowDefogWhirlpool = moves.Where((m, i) => IsDefogWhirl(m) && IsCheckInvalid(res[i])).Count() == 2;
|
||||
return;
|
||||
FlagIsHMSource(HMLearned, Legal.HM_4_RemovePokeTransfer);
|
||||
return moves.Where((m, i) => IsDefogWhirl(m) && IsCheckInvalid(parse[i])).Count() == 2;
|
||||
}
|
||||
KnowDefogWhirlpool = false;
|
||||
if (gen == 3 && pkm.Format > 3)
|
||||
IsHMSource(HMLearned, Legal.HM_3);
|
||||
FlagIsHMSource(HMLearned, Legal.HM_3);
|
||||
return false;
|
||||
|
||||
void IsHMSource(IList<bool> flags, ICollection<int> source)
|
||||
void FlagIsHMSource(Span<bool> flags, ICollection<int> source)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
flags[i] = IsCheckInvalid(res[i]) && source.Contains(moves[i]);
|
||||
for (int i = parse.Count - 1; i >= 0; i--)
|
||||
flags[i] = IsCheckInvalid(parse[i]) && source.Contains(moves[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDefogWhirl(int move) => move is (int)Move.Defog or (int)Move.Whirlpool;
|
||||
private static bool IsCheckInvalid(CheckResult? chk) => !(chk?.Valid ?? false);
|
||||
private static bool IsCheckValid(CheckResult? chk) => chk?.Valid ?? false;
|
||||
private static int GetDefogWhirlCount(IReadOnlyList<CheckMoveResult> parse, IReadOnlyList<int> moves)
|
||||
{
|
||||
int ctr = 0;
|
||||
for (int i = moves.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!IsDefogWhirl(moves[i]))
|
||||
continue;
|
||||
var r = parse[i];
|
||||
if (!r.Valid || r.Generation >= 5)
|
||||
continue;
|
||||
ctr++;
|
||||
}
|
||||
return ctr;
|
||||
}
|
||||
|
||||
private static void FlagIncompatibleTransferHMs45(CheckMoveResult[] res, IReadOnlyList<int> currentMoves, int gen, ReadOnlySpan<bool> HMLearned, bool KnowDefogWhirlpool)
|
||||
private static bool IsCheckInvalid(CheckMoveResult chk) => chk.IsParsed && !chk.Valid;
|
||||
private static bool IsCheckValid(CheckMoveResult chk) => chk.IsParsed && chk.Valid;
|
||||
|
||||
private static void FlagIncompatibleTransferHMs45(IReadOnlyList<CheckMoveResult> parse, IReadOnlyList<int> currentMoves, int gen, ReadOnlySpan<bool> HMLearned, bool KnowDefogWhirlpool)
|
||||
{
|
||||
// After all the moves from the generations 3 and 4,
|
||||
// including egg moves if is the origin generation because some hidden moves are also special egg moves in gen 3
|
||||
|
@ -663,36 +683,29 @@ namespace PKHeX.Core
|
|||
// the hidden move was learned in gen 3 or 4 but was not removed when transfer to 4 or 5
|
||||
if (KnowDefogWhirlpool)
|
||||
{
|
||||
int invalidCount = currentMoves.Where((m, i) => IsDefogWhirl(m) && IsCheckValid(res[i])).Count();
|
||||
int invalidCount = GetDefogWhirlCount(parse, currentMoves);
|
||||
if (invalidCount == 2) // can't know both at the same time
|
||||
{
|
||||
for (int i = 0; i < 4; i++) // flag both moves
|
||||
for (int i = parse.Count - 1; i >= 0; i--) // flag both moves
|
||||
{
|
||||
if (IsDefogWhirl(currentMoves[i]))
|
||||
res[i] = new CheckMoveResult(res[i], Invalid, LTransferMoveG4HM, CurrentMove);
|
||||
parse[i].FlagIllegal(LTransferMoveG4HM, CurrentMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flag moves that are only legal when learned from a past-gen HM source
|
||||
for (int i = 0; i < HMLearned.Length; i++)
|
||||
for (int i = HMLearned.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (HMLearned[i] && IsCheckValid(res[i]))
|
||||
res[i] = new CheckMoveResult(res[i], Invalid, string.Format(LTransferMoveHM, gen, gen + 1), CurrentMove);
|
||||
if (HMLearned[i] && IsCheckValid(parse[i]))
|
||||
parse[i].FlagIllegal(string.Format(LTransferMoveHM, gen, gen + 1), CurrentMove);
|
||||
}
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] VerifyPreRelearnEggBase(int[] currentMoves, EncounterEgg e)
|
||||
{
|
||||
CheckMoveResult[] result = new CheckMoveResult[4];
|
||||
_ = VerifyRelearnMoves.VerifyEggMoveset(e, result, currentMoves, CurrentMove);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void VerifyNoEmptyDuplicates(ReadOnlySpan<int> moves, CheckMoveResult[] res)
|
||||
private static void VerifyNoEmptyDuplicates(ReadOnlySpan<int> moves, ReadOnlySpan<CheckMoveResult> parse)
|
||||
{
|
||||
bool emptySlot = false;
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < parse.Length; i++)
|
||||
{
|
||||
var move = moves[i];
|
||||
if (move == 0)
|
||||
|
@ -704,34 +717,34 @@ namespace PKHeX.Core
|
|||
// If an empty slot was noted for a prior move, flag the empty slots.
|
||||
if (emptySlot)
|
||||
{
|
||||
FlagEmptySlotsBeforeIndex(moves, res, i);
|
||||
FlagEmptySlotsBeforeIndex(moves, parse, i);
|
||||
emptySlot = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for same move in next move slots
|
||||
FlagDuplicateMovesAfterIndex(moves, res, i, move);
|
||||
FlagDuplicateMovesAfterIndex(moves, parse, i, move);
|
||||
}
|
||||
}
|
||||
|
||||
private static void FlagDuplicateMovesAfterIndex(ReadOnlySpan<int> moves, CheckMoveResult[] res, int index, int move)
|
||||
private static void FlagDuplicateMovesAfterIndex(ReadOnlySpan<int> moves, ReadOnlySpan<CheckMoveResult> parse, int index, int move)
|
||||
{
|
||||
for (int i = index + 1; i < 4; i++)
|
||||
for (int i = parse.Length - 1; i > index; i--)
|
||||
{
|
||||
if (moves[i] != move)
|
||||
continue;
|
||||
res[index] = new CheckMoveResult(res[index], Invalid, LMoveSourceDuplicate);
|
||||
parse[index].FlagIllegal(LMoveSourceEmpty);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static void FlagEmptySlotsBeforeIndex(ReadOnlySpan<int> moves, CheckMoveResult[] res, int index)
|
||||
private static void FlagEmptySlotsBeforeIndex(ReadOnlySpan<int> moves, ReadOnlySpan<CheckMoveResult> parse, int index)
|
||||
{
|
||||
for (int i = index - 1; i >= 0; i--)
|
||||
{
|
||||
if (moves[i] != 0)
|
||||
return;
|
||||
res[i] = new CheckMoveResult(res[i], Invalid, LMoveSourceEmpty);
|
||||
parse[i].FlagIllegal(LMoveSourceEmpty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,9 @@ namespace PKHeX.Core
|
|||
/// </summary>
|
||||
public static class VerifyRelearnMoves
|
||||
{
|
||||
internal static readonly CheckResult DummyValid = new(CheckIdentifier.RelearnMove);
|
||||
private static readonly CheckResult DummyNone = new(Severity.Invalid, LMoveRelearnNone, CheckIdentifier.RelearnMove);
|
||||
internal static void DummyValid(CheckMoveResult p) => p.Set(MoveSource.Relearn, 0, Severity.Valid, L_AValid, CheckIdentifier.RelearnMove);
|
||||
|
||||
public static CheckResult[] VerifyRelearn(PKM pkm, IEncounterTemplate enc, CheckResult[] result)
|
||||
public static CheckMoveResult[] VerifyRelearn(PKM pkm, IEncounterTemplate enc, CheckMoveResult[] result)
|
||||
{
|
||||
if (ShouldNotHaveRelearnMoves(enc, pkm))
|
||||
return VerifyRelearnNone(pkm, result);
|
||||
|
@ -22,7 +21,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
IRelearn s when s.Relearn.Count != 0 => VerifyRelearnSpecifiedMoveset(pkm, s.Relearn, result),
|
||||
EncounterEgg e => VerifyEggMoveset(e, result, pkm.RelearnMoves),
|
||||
EncounterSlot6AO z when pkm.RelearnMove1 != 0 && z.CanDexNav => VerifyRelearnDexNav(pkm, result, z),
|
||||
EncounterSlot6AO {CanDexNav:true} z when pkm.RelearnMove1 != 0 => VerifyRelearnDexNav(pkm, result, z),
|
||||
EncounterSlot8b {IsUnderground:true} u => VerifyRelearnUnderground(pkm, result, u),
|
||||
_ => VerifyRelearnNone(pkm, result),
|
||||
};
|
||||
|
@ -30,63 +29,79 @@ namespace PKHeX.Core
|
|||
|
||||
public static bool ShouldNotHaveRelearnMoves(IGeneration enc, PKM pkm) => enc.Generation < 6 || pkm is IBattleVersion {BattleVersion: not 0};
|
||||
|
||||
private static CheckResult[] VerifyRelearnSpecifiedMoveset(PKM pkm, IReadOnlyList<int> required, CheckResult[] result)
|
||||
private static CheckMoveResult[] VerifyRelearnSpecifiedMoveset(PKM pkm, IReadOnlyList<int> required, CheckMoveResult[] result)
|
||||
{
|
||||
result[3] = CheckResult(pkm.RelearnMove4, required[3]);
|
||||
result[2] = CheckResult(pkm.RelearnMove3, required[2]);
|
||||
result[1] = CheckResult(pkm.RelearnMove2, required[1]);
|
||||
result[0] = CheckResult(pkm.RelearnMove1, required[0]);
|
||||
CheckResult(pkm.RelearnMove4, required[3], result[3]);
|
||||
CheckResult(pkm.RelearnMove3, required[2], result[2]);
|
||||
CheckResult(pkm.RelearnMove2, required[1], result[1]);
|
||||
CheckResult(pkm.RelearnMove1, required[0], result[0]);
|
||||
return result;
|
||||
|
||||
static CheckResult CheckResult(int move, int require)
|
||||
static void CheckResult(int move, int require, CheckMoveResult p)
|
||||
{
|
||||
if (move == require)
|
||||
return DummyValid;
|
||||
return new CheckResult(Severity.Invalid, string.Format(LMoveFExpect_0, MoveStrings[require]), CheckIdentifier.RelearnMove);
|
||||
{
|
||||
DummyValid(p);
|
||||
return;
|
||||
}
|
||||
var c = string.Format(LMoveFExpect_0, MoveStrings[require]);
|
||||
p.Set(MoveSource.Relearn, 0, Severity.Invalid, c, CheckIdentifier.RelearnMove);
|
||||
}
|
||||
}
|
||||
|
||||
private static CheckResult[] VerifyRelearnDexNav(PKM pkm, CheckResult[] result, EncounterSlot6AO slot)
|
||||
private static void ParseExpectEmpty(CheckMoveResult p, int move)
|
||||
{
|
||||
if (move == 0)
|
||||
DummyValid(p);
|
||||
else
|
||||
p.Set(MoveSource.Relearn, 0, Severity.Invalid, LMoveRelearnNone, CheckIdentifier.RelearnMove);
|
||||
}
|
||||
|
||||
private static CheckMoveResult[] VerifyRelearnDexNav(PKM pkm, CheckMoveResult[] result, EncounterSlot6AO slot)
|
||||
{
|
||||
// All other relearn moves must be empty.
|
||||
ParseExpectEmpty(result[3], pkm.RelearnMove4);
|
||||
ParseExpectEmpty(result[2], pkm.RelearnMove3);
|
||||
ParseExpectEmpty(result[1], pkm.RelearnMove2);
|
||||
|
||||
// DexNav Pokémon can have 1 random egg move as a relearn move.
|
||||
result[0] = !slot.CanBeDexNavMove(pkm.RelearnMove1) // not found
|
||||
? new CheckResult(Severity.Invalid, LMoveRelearnDexNav, CheckIdentifier.RelearnMove)
|
||||
: DummyValid;
|
||||
|
||||
// All other relearn moves must be empty.
|
||||
result[3] = pkm.RelearnMove4 == 0 ? DummyValid : DummyNone;
|
||||
result[2] = pkm.RelearnMove3 == 0 ? DummyValid : DummyNone;
|
||||
result[1] = pkm.RelearnMove2 == 0 ? DummyValid : DummyNone;
|
||||
var p = result[0];
|
||||
if (!slot.CanBeDexNavMove(pkm.RelearnMove1)) // not found
|
||||
p.Set(MoveSource.Relearn, 6, Severity.Invalid, LMoveRelearnDexNav, CheckIdentifier.RelearnMove);
|
||||
else
|
||||
DummyValid(p);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static CheckResult[] VerifyRelearnUnderground(PKM pkm, CheckResult[] result, EncounterSlot8b slot)
|
||||
private static CheckMoveResult[] VerifyRelearnUnderground(PKM pkm, CheckMoveResult[] result, EncounterSlot8b slot)
|
||||
{
|
||||
// Underground Pokémon can have 1 random egg move as a relearn move.
|
||||
result[0] = !slot.CanBeUndergroundMove(pkm.RelearnMove1) // not found
|
||||
? new CheckResult(Severity.Invalid, LMoveRelearnUnderground, CheckIdentifier.RelearnMove)
|
||||
: DummyValid;
|
||||
|
||||
// All other relearn moves must be empty.
|
||||
result[3] = pkm.RelearnMove4 == 0 ? DummyValid : DummyNone;
|
||||
result[2] = pkm.RelearnMove3 == 0 ? DummyValid : DummyNone;
|
||||
result[1] = pkm.RelearnMove2 == 0 ? DummyValid : DummyNone;
|
||||
ParseExpectEmpty(result[3], pkm.RelearnMove4);
|
||||
ParseExpectEmpty(result[2], pkm.RelearnMove3);
|
||||
ParseExpectEmpty(result[1], pkm.RelearnMove2);
|
||||
|
||||
// Underground Pokémon can have 1 random egg move as a relearn move.
|
||||
var p = result[0];
|
||||
if (!slot.CanBeUndergroundMove(pkm.RelearnMove1)) // not found
|
||||
p.Set(MoveSource.Relearn, 0, Severity.Invalid, LMoveRelearnUnderground, CheckIdentifier.RelearnMove);
|
||||
else
|
||||
DummyValid(p);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static CheckResult[] VerifyRelearnNone(PKM pkm, CheckResult[] result)
|
||||
private static CheckMoveResult[] VerifyRelearnNone(PKM pkm, CheckMoveResult[] result)
|
||||
{
|
||||
// No relearn moves should be present.
|
||||
result[3] = pkm.RelearnMove4 == 0 ? DummyValid : DummyNone;
|
||||
result[2] = pkm.RelearnMove3 == 0 ? DummyValid : DummyNone;
|
||||
result[1] = pkm.RelearnMove2 == 0 ? DummyValid : DummyNone;
|
||||
result[0] = pkm.RelearnMove1 == 0 ? DummyValid : DummyNone;
|
||||
ParseExpectEmpty(result[3], pkm.RelearnMove4);
|
||||
ParseExpectEmpty(result[2], pkm.RelearnMove3);
|
||||
ParseExpectEmpty(result[1], pkm.RelearnMove2);
|
||||
ParseExpectEmpty(result[0], pkm.RelearnMove1);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static CheckResult[] VerifyEggMoveset(EncounterEgg e, CheckResult[] result, int[] moves, CheckIdentifier type = CheckIdentifier.RelearnMove)
|
||||
internal static CheckMoveResult[] VerifyEggMoveset(EncounterEgg e, CheckMoveResult[] result, int[] moves, CheckIdentifier type = CheckIdentifier.RelearnMove)
|
||||
{
|
||||
int gen = e.Generation;
|
||||
var origins = MoveBreed.Process(gen, e.Species, e.Form, e.Version, moves, out var valid);
|
||||
|
@ -95,7 +110,7 @@ namespace PKHeX.Core
|
|||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var msg = EggSourceUtil.GetSource(origins, gen, i);
|
||||
result[i] = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Valid, msg, type);
|
||||
result[i].Set(MoveSource.EggMove, gen, Severity.Valid, msg, type);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -106,23 +121,22 @@ namespace PKHeX.Core
|
|||
{
|
||||
var msg = EggSourceUtil.GetSource(origins, gen, i);
|
||||
var expect = expected[i];
|
||||
CheckMoveResult line;
|
||||
var p = result[i];
|
||||
if (moves[i] == expect)
|
||||
{
|
||||
line = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Valid, msg, type);
|
||||
p.Set(MoveSource.EggMove, gen, Severity.Valid, msg, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = string.Format(LMoveRelearnFExpect_0, MoveStrings[expect], msg);
|
||||
line = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Invalid, msg, type);
|
||||
p.Set(MoveSource.EggMove, gen, Severity.Invalid, msg, type);
|
||||
}
|
||||
result[i] = line;
|
||||
}
|
||||
}
|
||||
|
||||
var dupe = IsAnyMoveDuplicate(moves);
|
||||
if (dupe != NO_DUPE)
|
||||
result[dupe] = new CheckMoveResult(MoveSource.EggMove, gen, Severity.Invalid, LMoveSourceDuplicate, type);
|
||||
result[dupe].Set(MoveSource.EggMove, gen, Severity.Invalid, LMoveSourceDuplicate, type);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary> Identification flair for what properties a <see cref="CheckResult"/> pertains to </summary>
|
||||
public enum CheckIdentifier
|
||||
public enum CheckIdentifier : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="CheckResult"/> pertains to the <see cref="PKM.Moves"/>.
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace PKHeX.Core
|
|||
/// Severity == <see cref="Fishy"/> is yellow
|
||||
/// Severity <= <see cref="Invalid"/> is red
|
||||
/// </remarks>
|
||||
public enum Severity
|
||||
public enum Severity : sbyte
|
||||
{
|
||||
/// <summary>
|
||||
/// Cannot determine validity; not valid.
|
||||
|
|
|
@ -365,7 +365,7 @@ namespace PKHeX.Core
|
|||
public static string LMoveRelearnUnderground { get; set; } = "Not an expected Underground egg move.";
|
||||
public static string LMoveRelearnEgg { get; set; } = "Base Egg move.";
|
||||
public static string LMoveRelearnEggMissing { get; set; } = "Base Egg move missing.";
|
||||
public static string LMoveRelearnFExpect_0 { get; set; } = "Expected the following Relearn Moves: {0} ({1}";
|
||||
public static string LMoveRelearnFExpect_0 { get; set; } = "Expected the following Relearn Moves: {0} ({1})";
|
||||
public static string LMoveRelearnFMiss_0 { get; set; } = "Relearn Moves missing: {0}";
|
||||
public static string LMoveRelearnInvalid { get; set; } = "Not an expected Relearnable move.";
|
||||
public static string LMoveRelearnNone { get; set; } = "Expected no Relearn Move in slot.";
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
public static void AddRelearn(CheckResult[] relearn, List<string> lines, bool state)
|
||||
public static void AddRelearn(CheckMoveResult[] relearn, List<string> lines, bool state)
|
||||
{
|
||||
for (int i = 0; i < relearn.Length; i++)
|
||||
{
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityAnalyzers;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
|
@ -101,9 +100,9 @@ namespace PKHeX.Core
|
|||
AddLine(Severity.Invalid, LEncConditionBadSpecies, CheckIdentifier.GameOrigin);
|
||||
GetParseMethod()();
|
||||
|
||||
Valid = Parse.All(chk => chk.Valid)
|
||||
&& Info.Moves.All(m => m.Valid)
|
||||
&& Info.Relearn.All(m => m.Valid);
|
||||
Valid = Parse.TrueForAll(chk => chk.Valid)
|
||||
&& Array.TrueForAll(Info.Moves, m => m.Valid)
|
||||
&& Array.TrueForAll(Info.Relearn, m => m.Valid);
|
||||
|
||||
if (!Valid && IsPotentiallyMysteryGift(Info, pkm))
|
||||
AddLine(Severity.Indeterminate, LFatefulGiftMissing, CheckIdentifier.Fateful);
|
||||
|
@ -116,17 +115,17 @@ namespace PKHeX.Core
|
|||
System.Diagnostics.Debug.WriteLine(e.Message);
|
||||
Valid = false;
|
||||
|
||||
var moves = Info.Moves;
|
||||
// Moves and Relearn arrays can potentially be empty on error.
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
for (int i = 0; i < moves.Length; i++)
|
||||
moves[i] ??= new CheckMoveResult(MoveSource.None, pkm.Format, Severity.Indeterminate, L_AError, CheckIdentifier.CurrentMove);
|
||||
|
||||
var relearn = Info.Relearn;
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
for (int i = 0; i < relearn.Length; i++)
|
||||
relearn[i] ??= new CheckResult(Severity.Indeterminate, L_AError, CheckIdentifier.RelearnMove);
|
||||
|
||||
foreach (var p in Info.Moves)
|
||||
{
|
||||
if (!p.IsParsed)
|
||||
p.Set(MoveSource.Unknown, pkm.Format, Severity.Indeterminate, L_AError, CheckIdentifier.CurrentMove);
|
||||
}
|
||||
foreach (var p in Info.Relearn)
|
||||
{
|
||||
if (!p.IsParsed)
|
||||
p.Set(MoveSource.Unknown, 0, Severity.Indeterminate, L_AError, CheckIdentifier.RelearnMove);
|
||||
}
|
||||
AddLine(Severity.Invalid, L_AError, CheckIdentifier.Misc);
|
||||
}
|
||||
#endif
|
||||
|
@ -142,7 +141,7 @@ namespace PKHeX.Core
|
|||
return false;
|
||||
if (enc.Generation < 6)
|
||||
return true;
|
||||
if (info.Relearn.Any(chk => !chk.Valid))
|
||||
if (Array.TrueForAll(info.Relearn, chk => !chk.Valid))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace PKHeX.Core
|
|||
/// Value passing object to simplify some initialization.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Egg Move source type enumeration.</typeparam>
|
||||
internal readonly ref struct BreedInfo<T> where T : Enum
|
||||
internal readonly ref struct BreedInfo<T> where T : unmanaged
|
||||
{
|
||||
/// <summary> Indicates the analyzed source of each move. </summary>
|
||||
public readonly T[] Actual;
|
||||
|
|
|
@ -1,49 +1,61 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Move specific <see cref="CheckResult"/> to contain in which Generation it was learned & source.
|
||||
/// </summary>
|
||||
public sealed record CheckMoveResult : ICheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Move specific <see cref="CheckResult"/> to contain in which Generation it was learned & source.
|
||||
/// </summary>
|
||||
public sealed class CheckMoveResult : CheckResult
|
||||
public Severity Judgement { get; private set; }
|
||||
public CheckIdentifier Identifier { get; private set; }
|
||||
public string Comment { get; private set; } = string.Empty;
|
||||
|
||||
public bool Valid => Judgement >= Severity.Fishy;
|
||||
public string Rating => Judgement.Description();
|
||||
|
||||
/// <summary> Method of learning the move. </summary>
|
||||
public MoveSource Source { get; private set; }
|
||||
|
||||
/// <summary> Generation the move was learned in. </summary>
|
||||
public int Generation { get; private set; }
|
||||
|
||||
/// <summary> Indicates if the source of the move was validated from the <see cref="PKM.RelearnMoves"/> </summary>
|
||||
public bool IsRelearn => Source == MoveSource.Relearn || (Source == MoveSource.EggMove && Generation >= 6);
|
||||
|
||||
/// <summary> Indicates if the source of the move was validated as originating from an egg. </summary>
|
||||
public bool IsEggSource => Source is MoveSource.EggMove or MoveSource.InheritLevelUp;
|
||||
|
||||
/// <summary> Indicates if the entry was parsed by the legality checker. Should never be true when the parent legality check is finalized. </summary>
|
||||
internal bool IsParsed => Source is not MoveSource.NotParsed;
|
||||
|
||||
/// <summary> Sets <see cref="IsParsed"/> to false. </summary>
|
||||
internal void Reset() => Source = MoveSource.NotParsed;
|
||||
|
||||
/// <summary> Checks if the Move should be present in a Relearn move pool (assuming Gen6+ origins). </summary>
|
||||
/// <remarks>Invalid moves that can't be validated should be here, hence the inclusion.</remarks>
|
||||
public bool ShouldBeInRelearnMoves() => Source != MoveSource.None && (!Valid || IsRelearn);
|
||||
|
||||
internal void Set(MoveSource m, int g, Severity s, string c, CheckIdentifier i)
|
||||
{
|
||||
/// <summary>
|
||||
/// Method of learning the move.
|
||||
/// </summary>
|
||||
public readonly MoveSource Source;
|
||||
|
||||
/// <summary>
|
||||
/// Generation the move was learned in.
|
||||
/// </summary>
|
||||
public readonly int Generation;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the source of the move was validated from the <see cref="PKM.RelearnMoves"/>
|
||||
/// </summary>
|
||||
public bool IsRelearn => Source == MoveSource.Relearn || (Source == MoveSource.EggMove && Generation >= 6);
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the source of the move was validated as originating from an egg.
|
||||
/// </summary>
|
||||
public bool IsEggSource => Source is MoveSource.EggMove or MoveSource.InheritLevelUp;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Move should be present in a Relearn move pool (assuming Gen6+ origins).
|
||||
/// </summary>
|
||||
/// <remarks>Invalid moves that can't be validated should be here, hence the inclusion.</remarks>
|
||||
public bool ShouldBeInRelearnMoves() => Source != MoveSource.None && (!Valid || IsRelearn);
|
||||
|
||||
internal CheckMoveResult(MoveSource m, int g, CheckIdentifier i) : base(i)
|
||||
{
|
||||
Source = m;
|
||||
Generation = g;
|
||||
}
|
||||
|
||||
internal CheckMoveResult(MoveSource m, int g, Severity s, string c, CheckIdentifier i) : base(s, c, i)
|
||||
{
|
||||
Source = m;
|
||||
Generation = g;
|
||||
}
|
||||
|
||||
internal CheckMoveResult(CheckMoveResult original, Severity s, string c, CheckIdentifier i) : this(original.Source, original.Generation, s, c, i) { }
|
||||
internal CheckMoveResult(CheckMoveResult original, Severity s, string c) : this(original.Source, original.Generation, s, c, original.Identifier) { }
|
||||
Judgement = s;
|
||||
Comment = c;
|
||||
Identifier = i;
|
||||
Source = m;
|
||||
Generation = g;
|
||||
}
|
||||
|
||||
internal void FlagIllegal(string comment)
|
||||
{
|
||||
Judgement = Severity.Invalid;
|
||||
Comment = comment;
|
||||
}
|
||||
|
||||
internal void FlagIllegal(string comment, CheckIdentifier identifier)
|
||||
{
|
||||
Judgement = Severity.Invalid;
|
||||
Comment = comment;
|
||||
Identifier = identifier;
|
||||
}
|
||||
|
||||
public string Format(string format) => string.Format(format, Rating, Comment);
|
||||
public string Format(string format, int index) => string.Format(format, Rating, index, Comment);
|
||||
}
|
||||
|
|
|
@ -1,34 +1,16 @@
|
|||
namespace PKHeX.Core
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Result of a Legality Check
|
||||
/// </summary>
|
||||
public sealed record CheckResult(Severity Judgement, string Comment, CheckIdentifier Identifier) : ICheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of a Legality Check
|
||||
/// </summary>
|
||||
public class CheckResult
|
||||
{
|
||||
public Severity Judgement { get; }
|
||||
public CheckIdentifier Identifier { get; }
|
||||
public string Comment { get; }
|
||||
public bool Valid => Judgement >= Severity.Fishy;
|
||||
public string Rating => Judgement.Description();
|
||||
|
||||
public bool Valid => Judgement >= Severity.Fishy;
|
||||
public string Rating => Judgement.Description();
|
||||
internal CheckResult(CheckIdentifier i) : this(Severity.Valid, LegalityCheckStrings.L_AValid, i) { }
|
||||
|
||||
public override string ToString() => $"{Identifier}: {Comment}";
|
||||
|
||||
internal CheckResult(CheckIdentifier i)
|
||||
{
|
||||
Judgement = Severity.Valid;
|
||||
Comment = LegalityCheckStrings.L_AValid;
|
||||
Identifier = i;
|
||||
}
|
||||
|
||||
internal CheckResult(Severity s, string c, CheckIdentifier i)
|
||||
{
|
||||
Judgement = s;
|
||||
Comment = c;
|
||||
Identifier = i;
|
||||
}
|
||||
|
||||
public string Format(string format) => string.Format(format, Rating, Comment);
|
||||
public string Format(string format, int index) => string.Format(format, Rating, index, Comment);
|
||||
}
|
||||
public override string ToString() => $"{Identifier}: {Comment}";
|
||||
public string Format(string format) => string.Format(format, Rating, Comment);
|
||||
public string Format(string format, int index) => string.Format(format, Rating, index, Comment);
|
||||
}
|
||||
|
|
11
PKHeX.Core/Legality/Structures/ICheckResult.cs
Normal file
11
PKHeX.Core/Legality/Structures/ICheckResult.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public interface ICheckResult
|
||||
{
|
||||
Severity Judgement { get; }
|
||||
CheckIdentifier Identifier { get; }
|
||||
string Comment { get; }
|
||||
|
||||
bool Valid { get; }
|
||||
string Rating { get; }
|
||||
}
|
|
@ -41,8 +41,17 @@ namespace PKHeX.Core
|
|||
/// <summary>Top level Legality Check result list for the <see cref="EncounterMatch"/>.</summary>
|
||||
internal readonly List<CheckResult> Parse;
|
||||
|
||||
public readonly CheckResult[] Relearn = new CheckResult[4];
|
||||
public CheckMoveResult[] Moves { get; internal set; } = new CheckMoveResult[4];
|
||||
private const int MoveCount = 4;
|
||||
public readonly CheckMoveResult[] Relearn = GetArray();
|
||||
public readonly CheckMoveResult[] Moves = GetArray();
|
||||
|
||||
private static CheckMoveResult[] GetArray()
|
||||
{
|
||||
var result = new CheckMoveResult[MoveCount];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = new CheckMoveResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly ValidEncounterMoves NONE = new();
|
||||
public ValidEncounterMoves EncounterMoves { get; internal set; } = NONE;
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
/// <summary>
|
||||
/// Indicates the method of learning a move
|
||||
/// </summary>
|
||||
public enum MoveSource
|
||||
public enum MoveSource : byte
|
||||
{
|
||||
NotParsed,
|
||||
Unknown,
|
||||
None,
|
||||
Relearn,
|
||||
|
|
|
@ -74,7 +74,7 @@ public sealed class LegendsArceusVerifier : Verifier
|
|||
{
|
||||
// Expected move should never be empty, but just future-proof against any revisions.
|
||||
var msg = expect[i] != 0 ? string.Format(LMoveFExpect_0, ParseSettings.MoveStrings[expect[i]]) : LMoveSourceEmpty;
|
||||
data.Info.Moves[i] = new CheckMoveResult(data.Info.Moves[i], Severity.Invalid, msg, CheckIdentifier.CurrentMove);
|
||||
data.Info.Moves[i].FlagIllegal(msg, CheckIdentifier.CurrentMove);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ namespace PKHeX.Core
|
|||
|
||||
var chk = Moves[index];
|
||||
if (chk.Generation == gen) // not obtained from a future gen
|
||||
Moves[index] = new CheckMoveResult(chk.Source, chk.Generation, Severity.Invalid, LTransferMove, CheckIdentifier.CurrentMove);
|
||||
Moves[index].FlagIllegal(LTransferMove, CheckIdentifier.CurrentMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -432,6 +432,14 @@ namespace PKHeX.Core
|
|||
|
||||
public int MoveCount => Convert.ToInt32(Move1 != 0) + Convert.ToInt32(Move2 != 0) + Convert.ToInt32(Move3 != 0) + Convert.ToInt32(Move4 != 0);
|
||||
|
||||
public void GetMoves(Span<int> value)
|
||||
{
|
||||
value[3] = Move4;
|
||||
value[2] = Move3;
|
||||
value[1] = Move2;
|
||||
value[0] = Move1;
|
||||
}
|
||||
|
||||
public void SetMoves(IReadOnlyList<int> value)
|
||||
{
|
||||
Move1 = value.Count > 0 ? value[0] : 0;
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace PKHeX.Tests.Legality
|
|||
if (isValid)
|
||||
{
|
||||
var info = legality.Info;
|
||||
var result = legality.Results.Concat(info.Moves).Concat(info.Relearn);
|
||||
var result = legality.Results.Cast<ICheckResult>().Concat(info.Moves).Concat(info.Relearn);
|
||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||
var invalid = result.Where(z => z?.Valid == false);
|
||||
var msg = string.Join(Environment.NewLine, invalid.Select(z => z.Comment));
|
||||
|
|
Loading…
Reference in a new issue