mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 22:54:14 +00:00
Refactor encounter matching
exercise in deferred execution/state machine, only calculate possible
matches until a sufficiently valid match is obtained. Previous setup
would try to calculate the 'best match' and had band-aid workarounds in
cases where a subsequent check may determine it to be a false match.
There's still more ways to improve speed:
- precalculate relationships for Encounter Slots rather than iterating
over every area
- yielding individual slots instead of an entire area
- group non-egg wondercards by ID in a dict/hashtable for faster
retrieval
reworked some internals:
- EncounterMatch is always an IEncounterable instead of an object, for
easy pattern matching.
- Splitbreed checking is done per encounter and is stored in the
EncounterEgg result
- Encounter validation uses Encounter/Move/RelearnMove/Evolution to
whittle to the final encounter.
As a part of the encounter matching, a lazy peek is used to check if an
invalid encounter should be retained instead of discarded; if another
encounter has not been checked, it'll stop the invalid checks and move
on. If it is the last encounter, no other valid encounters exist so it
will keep the parse for the invalid encounter.
If no encounters are yielded, then there is no encountermatch. An
EncounterInvalid is created to store basic details, and the parse is
carried out.
Breaks some legality checking features for flagging invalid moves in
more detail, but those can be re-added in a separate check (if
splitbreed & any move invalid -> check for other split moves).
Should now be easier to follow the flow & maintain 😄
This commit is contained in:
parent
56e11d920d
commit
858aa50689
25 changed files with 2957 additions and 3197 deletions
|
@ -9,29 +9,22 @@ namespace PKHeX.Core
|
|||
public partial class LegalityAnalysis
|
||||
{
|
||||
private PKM pkm;
|
||||
private DexLevel[][] EvoChainsAllGens;
|
||||
private readonly List<CheckResult> Parse = new List<CheckResult>();
|
||||
|
||||
private List<GBEncounterData> EncountersGBMatch;
|
||||
private object EncounterOriginalGB;
|
||||
private object EncounterMatch;
|
||||
private int EncounterSpecies;
|
||||
private IEncounterable EncounterOriginalGB;
|
||||
private IEncounterable EncounterMatch => info.EncounterMatch;
|
||||
private Type Type; // Parent class when applicable (EncounterStatic / MysteryGift)
|
||||
private Type MatchedType; // Child class if applicable (WC6, PGF, etc)
|
||||
private string EncounterName => Legal.getEncounterTypeName(pkm, EncounterOriginalGB ?? EncounterMatch);
|
||||
private List<MysteryGift> EventGiftMatch;
|
||||
private List<EncounterStatic> EncounterStaticMatch;
|
||||
private string EncounterName => Legal.getEncounterTypeName(EncounterOriginalGB ?? EncounterMatch);
|
||||
private CheckResult Encounter, History;
|
||||
private int[] RelearnBase;
|
||||
// private bool SecondaryChecked;
|
||||
|
||||
public readonly bool Parsed;
|
||||
public readonly bool Valid;
|
||||
public readonly bool Error;
|
||||
public LegalInfo info;
|
||||
public bool ParsedValid => Parsed && Valid;
|
||||
public bool ParsedInvalid => Parsed && !Valid;
|
||||
public CheckResult[] vMoves = new CheckResult[4];
|
||||
public CheckResult[] vRelearn = new CheckResult[4];
|
||||
public string Report(bool verbose = false) => verbose ? getVerboseLegalityReport() : getLegalityReport();
|
||||
private IEnumerable<int> AllSuggestedMoves
|
||||
{
|
||||
|
@ -51,7 +44,7 @@ namespace PKHeX.Core
|
|||
if (Error)
|
||||
return new int[4];
|
||||
if (_allSuggestedRelearnMoves == null)
|
||||
return _allSuggestedRelearnMoves = pkm == null || !pkm.IsOriginValid ? new int[4] : Legal.getValidRelearn(pkm, -1).ToArray();
|
||||
return _allSuggestedRelearnMoves = pkm == null || !pkm.IsOriginValid ? new int[4] : Legal.getValidRelearn(pkm, info.EncounterMatch.Species).ToArray();
|
||||
return _allSuggestedRelearnMoves;
|
||||
}
|
||||
}
|
||||
|
@ -60,12 +53,6 @@ namespace PKHeX.Core
|
|||
|
||||
public LegalityAnalysis(PKM pk)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
vMoves[i] = new CheckResult(CheckIdentifier.Move);
|
||||
vRelearn[i] = new CheckResult(CheckIdentifier.RelearnMove);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch (pk.Format) // prior to storing GameVersion
|
||||
|
@ -90,14 +77,14 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (Parse.Any(chk => !chk.Valid))
|
||||
Valid = false;
|
||||
else if (vMoves.Any(m => m.Valid != true))
|
||||
else if (info.vMoves.Any(m => m.Valid != true))
|
||||
Valid = false;
|
||||
else if (vRelearn.Any(m => m.Valid != true))
|
||||
else if (info.vRelearn.Any(m => m.Valid != true))
|
||||
Valid = false;
|
||||
else
|
||||
Valid = true;
|
||||
|
||||
if (pkm.FatefulEncounter && vRelearn.Any(chk => !chk.Valid) && EncounterMatch == null)
|
||||
if (pkm.FatefulEncounter && info.vRelearn.Any(chk => !chk.Valid) && EncounterMatch == null)
|
||||
AddLine(Severity.Indeterminate, V188, CheckIdentifier.Fateful);
|
||||
}
|
||||
}
|
||||
|
@ -126,9 +113,15 @@ namespace PKHeX.Core
|
|||
if (!pkm.IsOriginValid)
|
||||
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
|
||||
updateTradebackG12();
|
||||
updateEncounterChain();
|
||||
updateMoveLegality();
|
||||
|
||||
updateInfo();
|
||||
updateTypeInfo();
|
||||
if (pk.Format > 2) // transferred
|
||||
{
|
||||
EncounterOriginalGB = EncounterMatch;
|
||||
foreach (var z in verifyVCEncounter(pkm, EncounterMatch.Species, EncounterMatch as GBEncounterData))
|
||||
AddLine(z);
|
||||
}
|
||||
verifyNickname();
|
||||
verifyDVs();
|
||||
verifyG1OT();
|
||||
|
@ -140,10 +133,11 @@ namespace PKHeX.Core
|
|||
if (!pkm.IsOriginValid)
|
||||
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
|
||||
|
||||
updateEncounterChain();
|
||||
updateMoveLegality();
|
||||
updateInfo();
|
||||
updateTypeInfo();
|
||||
updateChecks();
|
||||
if (pkm.Format > 3)
|
||||
verifyTransferLegalityG3();
|
||||
|
||||
if (pkm.Version == 15)
|
||||
verifyCXD();
|
||||
|
@ -154,11 +148,11 @@ namespace PKHeX.Core
|
|||
if (!pkm.IsOriginValid)
|
||||
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
|
||||
|
||||
verifyPreRelearn();
|
||||
updateEncounterChain();
|
||||
updateMoveLegality();
|
||||
updateInfo();
|
||||
updateTypeInfo();
|
||||
updateChecks();
|
||||
if (pkm.Format > 4)
|
||||
verifyTransferLegalityG4();
|
||||
}
|
||||
private void parsePK5(PKM pk)
|
||||
{
|
||||
|
@ -166,9 +160,7 @@ namespace PKHeX.Core
|
|||
if (!pkm.IsOriginValid)
|
||||
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
|
||||
|
||||
verifyPreRelearn();
|
||||
updateEncounterChain();
|
||||
updateMoveLegality();
|
||||
updateInfo();
|
||||
updateTypeInfo();
|
||||
updateChecks();
|
||||
}
|
||||
|
@ -178,9 +170,7 @@ namespace PKHeX.Core
|
|||
if (!pkm.IsOriginValid)
|
||||
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
|
||||
|
||||
updateRelearnLegality();
|
||||
updateEncounterChain();
|
||||
updateMoveLegality();
|
||||
updateInfo();
|
||||
updateTypeInfo();
|
||||
updateChecks();
|
||||
}
|
||||
|
@ -190,35 +180,18 @@ namespace PKHeX.Core
|
|||
if (!pkm.IsOriginValid)
|
||||
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
|
||||
|
||||
updateRelearnLegality();
|
||||
updateEncounterChain();
|
||||
updateMoveLegality();
|
||||
updateInfo();
|
||||
updateTypeInfo();
|
||||
updateChecks();
|
||||
}
|
||||
|
||||
private void updateRelearnLegality()
|
||||
private void updateInfo()
|
||||
{
|
||||
try { vRelearn = verifyRelearn(); }
|
||||
catch { for (int i = 0; i < 4; i++) vRelearn[i] = new CheckResult(Severity.Invalid, V190, CheckIdentifier.RelearnMove); }
|
||||
// SecondaryChecked = false;
|
||||
}
|
||||
private void updateMoveLegality()
|
||||
{
|
||||
try { vMoves = verifyMoves(); }
|
||||
catch { for (int i = 0; i < 4; i++) vMoves[i] = new CheckResult(Severity.Invalid, V190, CheckIdentifier.Move); }
|
||||
// SecondaryChecked = false;
|
||||
info = EncounterFinder.verifyEncounter(pkm);
|
||||
Encounter = info.Parse[0];
|
||||
Parse.AddRange(info.Parse);
|
||||
}
|
||||
|
||||
private void updateEncounterChain()
|
||||
{
|
||||
if (EventGiftMatch?.Count > 1) // Multiple possible Mystery Gifts matched
|
||||
EncounterMatch = EventGiftMatch.First(); // temporarily set one so that Encounter can be verified
|
||||
|
||||
Encounter = verifyEncounter();
|
||||
Parse.Add(Encounter);
|
||||
EvoChainsAllGens = Legal.getEvolutionChainsAllGens(pkm, EncounterOriginalGB ?? EncounterMatch);
|
||||
}
|
||||
private void updateTradebackG12()
|
||||
{
|
||||
if (pkm.Format == 1)
|
||||
|
@ -278,13 +251,13 @@ namespace PKHeX.Core
|
|||
private void updateTypeInfo()
|
||||
{
|
||||
if (pkm.VC && pkm.Format == 7)
|
||||
EncounterMatch = Legal.getRBYStaticTransfer(pkm.Species);
|
||||
info.EncounterMatch = EncounterGenerator.getRBYStaticTransfer(pkm.Species);
|
||||
|
||||
if (pkm.GenNumber <= 2 && pkm.TradebackStatus == TradebackType.Any && EncountersGBMatch?.All(e => e.Generation != pkm.GenNumber) == true)
|
||||
if (pkm.GenNumber <= 2 && pkm.TradebackStatus == TradebackType.Any && (EncounterMatch as GBEncounterData)?.Generation != pkm.GenNumber)
|
||||
// Example: GSC Pokemon with only possible encounters in RBY, like the legendary birds
|
||||
pkm.TradebackStatus = TradebackType.WasTradeback;
|
||||
|
||||
MatchedType = Type = (EncounterOriginalGB ?? EncounterMatch ?? pkm.Species)?.GetType();
|
||||
MatchedType = Type = (EncounterOriginalGB ?? EncounterMatch)?.GetType();
|
||||
var bt = Type.GetTypeInfo().BaseType;
|
||||
if (bt != null && !(bt == typeof(Array) || bt == typeof(object) || bt.GetTypeInfo().IsPrimitive)) // a parent exists
|
||||
Type = bt; // use base type
|
||||
|
@ -326,6 +299,8 @@ namespace PKHeX.Core
|
|||
return V189;
|
||||
|
||||
var lines = new List<string>();
|
||||
var vMoves = info.vMoves;
|
||||
var vRelearn = info.vRelearn;
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (!vMoves[i].Valid)
|
||||
lines.Add(string.Format(V191, getString(vMoves[i].Judgement), i + 1, vMoves[i].Comment));
|
||||
|
@ -358,6 +333,8 @@ namespace PKHeX.Core
|
|||
lines.AddRange(br);
|
||||
int rl = lines.Count;
|
||||
|
||||
var vMoves = info.vMoves;
|
||||
var vRelearn = info.vRelearn;
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (vMoves[i].Valid)
|
||||
lines.Add(string.Format(V191, getString(vMoves[i].Judgement), i + 1, vMoves[i].Comment));
|
||||
|
@ -388,13 +365,14 @@ namespace PKHeX.Core
|
|||
|
||||
public int[] getSuggestedRelearn()
|
||||
{
|
||||
if (RelearnBase == null || pkm.GenNumber < 6 || !pkm.IsOriginValid)
|
||||
if (info.RelearnBase == null || pkm.GenNumber < 6 || !pkm.IsOriginValid)
|
||||
return new int[4];
|
||||
|
||||
if (!pkm.WasEgg)
|
||||
return RelearnBase;
|
||||
return info.RelearnBase;
|
||||
|
||||
List<int> window = new List<int>(RelearnBase);
|
||||
List<int> window = new List<int>(info.RelearnBase);
|
||||
var vMoves = info.vMoves;
|
||||
window.AddRange(pkm.Moves.Where((v, i) => !vMoves[i].Valid || vMoves[i].Flag));
|
||||
window = window.Distinct().ToList();
|
||||
if (window.Count < 4)
|
||||
|
@ -407,7 +385,7 @@ namespace PKHeX.Core
|
|||
return null;
|
||||
if (!Parsed)
|
||||
return new int[4];
|
||||
return Legal.getValidMoves(pkm, EvoChainsAllGens, Tutor: tutor, Machine: tm, MoveReminder: reminder).Skip(1).ToArray(); // skip move 0
|
||||
return Legal.getValidMoves(pkm, info.EvoChainsAllGens, Tutor: tutor, Machine: tm, MoveReminder: reminder).Skip(1).ToArray(); // skip move 0
|
||||
}
|
||||
|
||||
public EncounterStatic getSuggestedMetInfo()
|
||||
|
@ -431,7 +409,7 @@ namespace PKHeX.Core
|
|||
};
|
||||
}
|
||||
|
||||
var area = Legal.getCaptureLocation(pkm);
|
||||
var area = EncounterGenerator.getCaptureLocation(pkm);
|
||||
if (area != null)
|
||||
{
|
||||
var slots = area.Slots.OrderBy(s => s.LevelMin);
|
||||
|
@ -443,7 +421,7 @@ namespace PKHeX.Core
|
|||
};
|
||||
}
|
||||
|
||||
var encounter = Legal.getStaticLocation(pkm);
|
||||
var encounter = EncounterGenerator.getStaticLocation(pkm);
|
||||
if (loc != -1 && encounter != null)
|
||||
encounter.Location = loc;
|
||||
return encounter;
|
||||
|
@ -506,21 +484,5 @@ namespace PKHeX.Core
|
|||
return 30001;
|
||||
return -1;
|
||||
}
|
||||
private static int[] getGenMovesCheckOrder(PKM pkm)
|
||||
{
|
||||
if (pkm.Format == 1)
|
||||
return new [] { 1, 2 };
|
||||
if (pkm.Format == 2)
|
||||
return new [] { 2, 1 };
|
||||
if (pkm.Format == 7 && pkm.VC1)
|
||||
return new [] { 7, 1 };
|
||||
if (pkm.Format == 7 && pkm.VC2)
|
||||
return new [] { 7, 2, 1 };
|
||||
|
||||
var order = new int[pkm.Format - pkm.GenNumber + 1];
|
||||
for (int i = 0; i < order.Length; i++)
|
||||
order[i] = pkm.Format - i;
|
||||
return order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
15
PKHeX.Core/Legality/Encounters/EncounterEgg.cs
Normal file
15
PKHeX.Core/Legality/Encounters/EncounterEgg.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public class EncounterEgg : IEncounterable
|
||||
{
|
||||
public int Species { get; set; }
|
||||
public string Name => "Egg";
|
||||
public bool EggEncounter => true;
|
||||
public int LevelMin => Level;
|
||||
public int LevelMax => Level;
|
||||
public int Level;
|
||||
|
||||
public GameVersion Game;
|
||||
public bool SplitBreed;
|
||||
}
|
||||
}
|
88
PKHeX.Core/Legality/Encounters/EncounterFinder.cs
Normal file
88
PKHeX.Core/Legality/Encounters/EncounterFinder.cs
Normal file
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class EncounterFinder
|
||||
{
|
||||
public static LegalInfo verifyEncounter(PKM pkm)
|
||||
{
|
||||
LegalInfo info = new LegalInfo(pkm);
|
||||
var encounters = EncounterGenerator.getEncounters(pkm, info);
|
||||
|
||||
using (var encounter = new PeekEnumerator<IEncounterable>(encounters.GetEnumerator()))
|
||||
{
|
||||
if (!encounter.PeekIsNext())
|
||||
return verifyWithoutEncounter(pkm, info);
|
||||
|
||||
var EncounterValidator = getEncounterVerifier(pkm);
|
||||
while (encounter.MoveNext())
|
||||
{
|
||||
var EncounterMatch = info.EncounterMatch = encounter.Current;
|
||||
|
||||
var e = EncounterValidator(pkm, EncounterMatch);
|
||||
if (!e.Valid && encounter.PeekIsNext())
|
||||
continue;
|
||||
info.Parse.Add(e);
|
||||
|
||||
if (pkm.Format >= 6)
|
||||
{
|
||||
info.vRelearn = VerifyRelearnMoves.verifyRelearn(pkm, info);
|
||||
if (info.vRelearn.Any(z => !z.Valid) && encounter.PeekIsNext())
|
||||
continue;
|
||||
}
|
||||
else
|
||||
for (int i = 0; i < 4; i++)
|
||||
info.vRelearn[i] = new CheckResult(CheckIdentifier.RelearnMove);
|
||||
|
||||
info.vMoves = VerifyCurrentMoves.verifyMoves(pkm, info);
|
||||
if (info.vMoves.Any(z => !z.Valid) && encounter.PeekIsNext())
|
||||
continue;
|
||||
|
||||
var evo = VerifyEvolution.verifyEvolution(pkm, EncounterMatch);
|
||||
if (!evo.Valid && encounter.PeekIsNext())
|
||||
continue;
|
||||
|
||||
info.Parse.Add(evo);
|
||||
|
||||
// Encounter Passes
|
||||
break;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
private static LegalInfo verifyWithoutEncounter(PKM pkm, LegalInfo info)
|
||||
{
|
||||
info.EncounterMatch = new EncounterInvalid(pkm);
|
||||
|
||||
string hint; // hint why an encounter was not found
|
||||
if (pkm.WasGiftEgg)
|
||||
hint = V359;
|
||||
else if (pkm.WasEventEgg)
|
||||
hint = V360;
|
||||
else if (pkm.WasEvent)
|
||||
hint = V78;
|
||||
else
|
||||
hint = V80;
|
||||
|
||||
info.Parse.Add(new CheckResult(Severity.Invalid, hint, CheckIdentifier.Encounter));
|
||||
info.vRelearn = VerifyRelearnMoves.verifyRelearn(pkm, info);
|
||||
info.vMoves = VerifyCurrentMoves.verifyMoves(pkm, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
private static Func<PKM, IEncounterable, CheckResult> getEncounterVerifier(PKM pkm)
|
||||
{
|
||||
switch (pkm.GenNumber)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
return VerifyEncounter.verifyEncounterG12;
|
||||
default:
|
||||
return VerifyEncounter.verifyEncounter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1092
PKHeX.Core/Legality/Encounters/EncounterGenerator.cs
Normal file
1092
PKHeX.Core/Legality/Encounters/EncounterGenerator.cs
Normal file
File diff suppressed because it is too large
Load diff
18
PKHeX.Core/Legality/Encounters/EncounterInvalid.cs
Normal file
18
PKHeX.Core/Legality/Encounters/EncounterInvalid.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public class EncounterInvalid : IEncounterable
|
||||
{
|
||||
public int Species { get; }
|
||||
public string Name => "Invalid";
|
||||
public bool EggEncounter => false;
|
||||
public int LevelMin => Level;
|
||||
public int LevelMax => Level;
|
||||
public readonly int Level;
|
||||
|
||||
public EncounterInvalid(PKM pkm)
|
||||
{
|
||||
Species = pkm.Species;
|
||||
Level = pkm.CurrentLevel;
|
||||
}
|
||||
}
|
||||
}
|
42
PKHeX.Core/Legality/Encounters/LegalInfo.cs
Normal file
42
PKHeX.Core/Legality/Encounters/LegalInfo.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public class LegalInfo
|
||||
{
|
||||
/// <summary>The <see cref="PKM"/> object used for comparisons.</summary>
|
||||
private readonly PKM pkm;
|
||||
|
||||
/// <summary>The generation of games the PKM originated from.</summary>
|
||||
public int Generation;
|
||||
|
||||
/// <summary>The matched Encounter details for the <see cref="PKM"/>. </summary>
|
||||
public IEncounterable EncounterMatch
|
||||
{
|
||||
get => _match;
|
||||
set
|
||||
{
|
||||
if (EncounterMatch != null && (value.LevelMin != EncounterMatch.LevelMin || value.Species != EncounterMatch.Species))
|
||||
_evochains = null;
|
||||
_match = value;
|
||||
Parse.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int[] RelearnBase;
|
||||
|
||||
public readonly List<CheckResult> Parse = new List<CheckResult>();
|
||||
|
||||
public CheckResult[] vRelearn = new CheckResult[4];
|
||||
public CheckResult[] vMoves = new CheckResult[4];
|
||||
|
||||
public DexLevel[][] EvoChainsAllGens => _evochains ?? (_evochains = Legal.getEvolutionChainsAllGens(pkm, EncounterMatch));
|
||||
public ValidEncounterMoves EncounterMoves { get; set; }
|
||||
|
||||
private DexLevel[][] _evochains;
|
||||
private IEncounterable _match;
|
||||
public PIDIV PIDIV;
|
||||
|
||||
public LegalInfo(PKM pk) => pkm = pk;
|
||||
}
|
||||
}
|
61
PKHeX.Core/Legality/Encounters/PeekEnumerator.cs
Normal file
61
PKHeX.Core/Legality/Encounters/PeekEnumerator.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public class PeekEnumerator<T> : IEnumerator<T>
|
||||
{
|
||||
private readonly IEnumerator<T> Enumerator;
|
||||
private T peek;
|
||||
private bool didPeek;
|
||||
|
||||
#region IEnumerator Implementation
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (!didPeek)
|
||||
return Enumerator.MoveNext();
|
||||
didPeek = false;
|
||||
return true;
|
||||
}
|
||||
public void Reset()
|
||||
{
|
||||
Enumerator.Reset();
|
||||
didPeek = false;
|
||||
}
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
public void Dispose() => Enumerator.Dispose();
|
||||
public T Current => didPeek ? peek : Enumerator.Current;
|
||||
|
||||
#endregion
|
||||
|
||||
public PeekEnumerator(IEnumerator<T> enumerator) => Enumerator = enumerator ?? throw new ArgumentNullException(nameof(enumerator));
|
||||
|
||||
private void TryFetchPeek()
|
||||
{
|
||||
if (!didPeek && (didPeek = Enumerator.MoveNext()))
|
||||
peek = Enumerator.Current;
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
TryFetchPeek();
|
||||
if (!didPeek)
|
||||
throw new InvalidOperationException("Enumeration already finished.");
|
||||
|
||||
return peek;
|
||||
}
|
||||
public T PeekOrDefault()
|
||||
{
|
||||
TryFetchPeek();
|
||||
return !didPeek ? default(T) : peek;
|
||||
}
|
||||
public bool PeekIsNext()
|
||||
{
|
||||
TryFetchPeek();
|
||||
return didPeek;
|
||||
}
|
||||
}
|
||||
}
|
750
PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs
Normal file
750
PKHeX.Core/Legality/Encounters/VerifyCurrentMoves.cs
Normal file
|
@ -0,0 +1,750 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityAnalysis;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class VerifyCurrentMoves
|
||||
{
|
||||
public static CheckResult[] verifyMoves(PKM pkm, LegalInfo info, GameVersion game = GameVersion.Any)
|
||||
{
|
||||
int[] Moves = pkm.Moves;
|
||||
var res = parseMovesForEncounters(pkm, info, game, Moves);
|
||||
|
||||
// Duplicate Moves Check
|
||||
verifyNoEmptyDuplicates(Moves, res);
|
||||
if (Moves[0] == 0) // Can't have an empty moveslot for the first move.
|
||||
res[0] = new CheckResult(Severity.Invalid, V167, CheckIdentifier.Move);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static CheckResult[] parseMovesForEncounters(PKM pkm, LegalInfo info, GameVersion game, int[] Moves)
|
||||
{
|
||||
if (pkm.Species == 235) // special handling for Smeargle
|
||||
return parseMovesForSmeargle(pkm, Moves, info); // Smeargle can have any moves except a few
|
||||
|
||||
// Iterate over encounters
|
||||
bool pre3DS = pkm.GenNumber < 6;
|
||||
|
||||
// gather valid moves for encounter species
|
||||
|
||||
info.EncounterMoves = getEncounterValidMoves(pkm, info);
|
||||
|
||||
if (pkm.GenNumber <= 3)
|
||||
pkm.WasEgg = info.EncounterMatch.EggEncounter;
|
||||
|
||||
var EncounterMatchGen = info.EncounterMatch as IGeneration;
|
||||
var defaultG1LevelMoves = info.EncounterMoves.validLevelUpMoves[1];
|
||||
var defaultTradeback = pkm.TradebackStatus;
|
||||
if (EncounterMatchGen != null)
|
||||
// Generation 1 can have different minimum level in different encounter of the same species; update valid level moves
|
||||
UptateGen1LevelUpMoves(pkm, info.EncounterMoves, info.EncounterMoves.minLvlG1, EncounterMatchGen.Generation, info);
|
||||
|
||||
var res = pre3DS
|
||||
? parseMovesPre3DS(pkm, Moves, info)
|
||||
: parseMoves3DS(pkm, game, Moves, info);
|
||||
|
||||
if (res.All(x => x.Valid))
|
||||
return res;
|
||||
|
||||
if (EncounterMatchGen?.Generation == 1) // not valid, restore generation 1 moves
|
||||
info.EncounterMoves.validLevelUpMoves[1] = defaultG1LevelMoves;
|
||||
pkm.TradebackStatus = defaultTradeback;
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] parseMovesForSmeargle(PKM pkm, int[] Moves, LegalInfo info)
|
||||
{
|
||||
if (!pkm.IsEgg)
|
||||
return parseMovesSketch(Moves);
|
||||
|
||||
// can only know sketch as egg
|
||||
var empty = ValidEncounterMoves.Empty;
|
||||
info.EncounterMoves = new ValidEncounterMoves
|
||||
{
|
||||
validLevelUpMoves = Legal.getValidMovesAllGens(pkm, info.EvoChainsAllGens, minLvLG1: 1, Tutor: false, Machine: false, RemoveTransferHM: false)
|
||||
};
|
||||
return parseMoves(pkm, pkm.Moves, new int[0], new int[0], new int[0], empty, new int[0], new int[0], false, info);
|
||||
}
|
||||
private static CheckResult[] parseMovesIsEggPreRelearn(PKM pkm, int[] Moves, int[] SpecialMoves, bool allowinherited, EncounterEgg e)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
var ValidSpecialMoves = SpecialMoves.Where(m => m != 0).ToList();
|
||||
|
||||
var baseEggMoves = Legal.getBaseEggMoves(pkm, e.Species, e.Game, pkm.GenNumber < 4 ? 5 : 1)?.ToList() ?? new List<int>();
|
||||
var InheritedLvlMoves = Legal.getBaseEggMoves(pkm, e.Species, e.Game, 100)?.ToList() ?? new List<int>();
|
||||
var EggMoves = Legal.getEggMoves(pkm, e.Species, pkm.AltForm)?.ToList() ?? new List<int>();
|
||||
var InheritedTutorMoves = e.Game == GameVersion.C ? Legal.getTutorMoves(pkm, pkm.Species, pkm.AltForm, false, 2)?.ToList() : new List<int>();
|
||||
// Only TM Hm moves from the source game of the egg, not any other games from the same generation
|
||||
var InheritedTMHMMoves = Legal.getTMHM(pkm, pkm.Species, pkm.AltForm, pkm.GenNumber, e.Game, false)?.ToList();
|
||||
InheritedLvlMoves.RemoveAll(x => baseEggMoves.Contains(x));
|
||||
|
||||
if (pkm.Format > 2 || SpecialMoves.Any())
|
||||
{
|
||||
// For gen 2 is not possible to difference normal eggs from event eggs
|
||||
// If there is no special moves assume normal egg
|
||||
res = verifyPreRelearnEggBase(pkm, Moves, baseEggMoves, EggMoves, InheritedLvlMoves, InheritedTMHMMoves, InheritedTutorMoves, ValidSpecialMoves, allowinherited, e.Game);
|
||||
}
|
||||
else if (pkm.Format == 2)
|
||||
{
|
||||
res = verifyPreRelearnEggBase(pkm, Moves, baseEggMoves, EggMoves, InheritedLvlMoves, InheritedTMHMMoves, InheritedTutorMoves, new List<int>(), true, e.Game);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] parseMovesWasEggPreRelearn(PKM pkm, int[] Moves, LegalInfo info, EncounterEgg e)
|
||||
{
|
||||
var EventEggMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0];
|
||||
int BaseLvlMoves = 489 <= pkm.Species && pkm.Species <= 490 ? 1 : 100;
|
||||
var LvlupEggMoves = Legal.getBaseEggMoves(pkm, e.Species, e.Game, BaseLvlMoves);
|
||||
// Level up, TMHM or tutor moves exclusive to the incense egg species, like Azurill, incompatible with the non-incense species egg moves
|
||||
var ExclusiveIncenseMoves = e.SplitBreed ? Legal.getExclusivePreEvolutionMoves(pkm, Legal.getBaseEggSpecies(pkm), info.EvoChainsAllGens, e.Game) : null;
|
||||
var EggMoves = Legal.getEggMoves(pkm, e.Species, pkm.AltForm);
|
||||
|
||||
bool volt = (pkm.GenNumber > 3 || e.Game == GameVersion.E) && Legal.LightBall.Contains(pkm.Species);
|
||||
var SpecialMoves = volt && EventEggMoves.Length == 0 ? new[] { 344 } : new int[0]; // Volt Tackle for bred Pichu line
|
||||
|
||||
return parseMoves(pkm, Moves, SpecialMoves, LvlupEggMoves, EggMoves, ExclusiveIncenseMoves, EventEggMoves, new int[0], e.SplitBreed, info);
|
||||
}
|
||||
private static CheckResult[] parseMovesSketch(int[] Moves)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
res[i] = Legal.InvalidSketch.Contains(Moves[i])
|
||||
? new CheckResult(Severity.Invalid, V166, CheckIdentifier.Move)
|
||||
: new CheckResult(CheckIdentifier.Move);
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] parseMoves3DS(PKM pkm, GameVersion game, int[] Moves, LegalInfo info)
|
||||
{
|
||||
info.EncounterMoves.Relearn = pkm.GenNumber >= 6 ? pkm.RelearnMoves : new int[0];
|
||||
if (info.EncounterMatch is IMoveset)
|
||||
return parseMovesSpecialMoveset(pkm, Moves, info);
|
||||
|
||||
// Everything else
|
||||
return parseMovesRelearn(pkm, Moves, info);
|
||||
}
|
||||
private static CheckResult[] parseMovesPre3DS(PKM pkm, int[] Moves, LegalInfo info)
|
||||
{
|
||||
if (pkm.IsEgg)
|
||||
{
|
||||
int[] SpecialMoves = (info.EncounterMatch as IMoveset)?.Moves;
|
||||
// Gift do not have special moves but also should not have normal egg moves
|
||||
var allowinherited = SpecialMoves == null && !pkm.WasGiftEgg && pkm.Species != 489 && pkm.Species != 490;
|
||||
return parseMovesIsEggPreRelearn(pkm, Moves, SpecialMoves ?? new int[0], allowinherited, info.EncounterMatch as EncounterEgg);
|
||||
}
|
||||
if (pkm.GenNumber <= 2 && (info.EncounterMatch as IGeneration)?.Generation == 1)
|
||||
return parseMovesGen1(pkm, Moves, info);
|
||||
if (info.EncounterMatch is EncounterEgg e)
|
||||
return parseMovesWasEggPreRelearn(pkm, Moves, info, e);
|
||||
|
||||
return parseMovesSpecialMoveset(pkm, Moves, info);
|
||||
}
|
||||
private static CheckResult[] parseMovesGen1(PKM pkm, int[] Moves, LegalInfo info)
|
||||
{
|
||||
GameVersion[] games = Legal.getGen1GameEncounter(pkm);
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
var G1Encounter = info.EncounterMatch;
|
||||
if (G1Encounter == null)
|
||||
return parseMovesSpecialMoveset(pkm, Moves, info);
|
||||
var InitialMoves = new int[0];
|
||||
int[] SpecialMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0];
|
||||
var empty = Legal.GetEmptyMovesList(info.EvoChainsAllGens);
|
||||
var emptyegg = new int[0];
|
||||
foreach (GameVersion ver in games)
|
||||
{
|
||||
var VerInitialMoves = Legal.getInitialMovesGBEncounter(G1Encounter.Species, G1Encounter.LevelMin, ver).ToArray();
|
||||
if (VerInitialMoves.SequenceEqual(InitialMoves))
|
||||
return res;
|
||||
res = parseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, empty, new int[0], VerInitialMoves, false, info);
|
||||
if (res.All(r => r.Valid))
|
||||
return res;
|
||||
InitialMoves = VerInitialMoves;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] parseMovesSpecialMoveset(PKM pkm, int[] Moves, LegalInfo info)
|
||||
{
|
||||
var mg = info.EncounterMatch as IMoveset;
|
||||
int[] SpecialMoves = mg?.Moves ?? new int[0];
|
||||
var empty = Legal.GetEmptyMovesList(info.EvoChainsAllGens);
|
||||
var emptyegg = new int[0];
|
||||
CheckResult[] res = parseMoves(pkm, Moves, SpecialMoves, emptyegg, emptyegg, empty, new int[0], new int[0], false, info);
|
||||
if (res.Any(r => !r.Valid))
|
||||
{
|
||||
info.RelearnBase = new int[4];
|
||||
return res;
|
||||
}
|
||||
|
||||
if (pkm.GenNumber >= 6 && info.EncounterMatch is MysteryGift z)
|
||||
info.RelearnBase = z.RelearnMoves;
|
||||
else
|
||||
info.RelearnBase = new int[4];
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] parseMovesRelearn(PKM pkm, int[] Moves, LegalInfo info)
|
||||
{
|
||||
var emptyegg = new int[0];
|
||||
|
||||
var issplitbreed = pkm.WasEgg && Legal.SplitBreed.Contains(pkm.Species);
|
||||
var e = info.EncounterMatch as EncounterEgg;
|
||||
var EggMoves = e != null ? Legal.getEggMoves(pkm, e.Species, pkm.AltForm, e.Game) : emptyegg;
|
||||
// Level up, TMHM or tutor moves exclusive to the incense egg species, like Azurill, incompatible with the non-incense species egg moves
|
||||
var ExclusiveIncenseMoves = issplitbreed ? Legal.getExclusivePreEvolutionMoves(pkm, Legal.getBaseEggSpecies(pkm), info.EvoChainsAllGens, e.Game) : Legal.GetEmptyMovesList(info.EvoChainsAllGens);
|
||||
|
||||
int[] RelearnMoves = pkm.RelearnMoves;
|
||||
int[] SpecialMoves = (info.EncounterMatch as IMoveset)?.Moves ?? new int[0];
|
||||
|
||||
CheckResult[] res = parseMoves(pkm, Moves, SpecialMoves, new int[0], EggMoves, ExclusiveIncenseMoves, new int[0], new int[0], issplitbreed, info);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
if ((pkm.IsEgg || res[i].Flag) && !RelearnMoves.Contains(Moves[i]))
|
||||
res[i] = new CheckResult(Severity.Invalid, string.Format(V170, res[i].Comment), res[i].Identifier);
|
||||
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] parseMoves(PKM pkm, int[] moves, int[] special, int[] lvlupegg, int[] egg, List<int>[] IncenseExclusiveMoves, int[] eventegg, int[] initialmoves, bool issplitbreed, LegalInfo info)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
var Gen1MovesLearned = new List<int>();
|
||||
var EggMovesLearned = new List<int>();
|
||||
var LvlupEggMovesLearned = new List<int>();
|
||||
var EventEggMovesLearned = new List<int>();
|
||||
var IsGen2Pkm = pkm.Format == 2 || pkm.VC2;
|
||||
var required = Legal.getRequiredMoveCount(pkm, moves, info, initialmoves);
|
||||
var EggMovesSplitLearned = new List<int>[ValidEncounterMoves.Empty.Length];
|
||||
var IncenseMovesLearned = new List<int>();
|
||||
|
||||
// Check none moves and relearn moves before generation moves
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (moves[m] == 0)
|
||||
res[m] = new CheckResult(m < required ? Severity.Invalid : Severity.Valid, V167, CheckIdentifier.Move);
|
||||
else if (info.EncounterMoves.Relearn.Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Valid, V172, CheckIdentifier.Move) { Flag = true };
|
||||
}
|
||||
|
||||
if (res.All(r => r != null))
|
||||
return res;
|
||||
|
||||
bool MixedGen1NonTradebackGen2 = false;
|
||||
// Check moves going backwards, marking the move valid in the most current generation when it can be learned
|
||||
int[] generations = getGenMovesCheckOrder(pkm);
|
||||
foreach (var gen in generations)
|
||||
{
|
||||
if (!pkm.InhabitedGeneration(gen))
|
||||
continue;
|
||||
|
||||
var HMLearned = new int[0];
|
||||
// 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
|
||||
bool KnowDefogWhirlpool = false;
|
||||
if (gen == 4 && pkm.Format > 4)
|
||||
{
|
||||
// Copy to array the hm found or else the list will be emptied when the legal status of moves changes in the current generation
|
||||
HMLearned = moves.Where((m, i) => !(res[i]?.Valid ?? false) && Legal.HM_4_RemovePokeTransfer.Any(l => l == m)).Select((m, i) => i).ToArray();
|
||||
// Defog and Whirlpool at the same time, also both can't be learned in future generations or else they will be valid
|
||||
KnowDefogWhirlpool = moves.Where((m, i) => (m == 250 || m == 432) && !(res[i]?.Valid ?? false)).Count() == 2;
|
||||
}
|
||||
else if (gen == 3 && pkm.Format > 3)
|
||||
HMLearned = moves.Select((m, i) => i).Where(i => !(res[i]?.Valid ?? false) && Legal.HM_3.Any(l => l == moves[i])).ToArray();
|
||||
|
||||
bool native = gen == pkm.Format;
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (res[m]?.Valid ?? false)
|
||||
continue;
|
||||
if (moves[m] == 0)
|
||||
continue;
|
||||
|
||||
if (gen == 1 && initialmoves.Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Valid, native ? V361 : string.Format(V362, gen), CheckIdentifier.Move);
|
||||
else if (info.EncounterMoves.validLevelUpMoves[gen].Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Valid, native ? V177 : string.Format(V330, gen), CheckIdentifier.Move);
|
||||
else if (info.EncounterMoves.validTMHMMoves[gen].Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Valid, native ? V173 : string.Format(V331, gen), CheckIdentifier.Move);
|
||||
else if (info.EncounterMoves.validTutorMoves[gen].Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Valid, native ? V174 : string.Format(V332, gen), CheckIdentifier.Move);
|
||||
else if (gen == pkm.GenNumber && special.Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Valid, V175, CheckIdentifier.Move);
|
||||
|
||||
if (res[m] == null || gen < 3)
|
||||
continue;
|
||||
|
||||
if (res[m].Valid && gen == 1)
|
||||
Gen1MovesLearned.Add(m);
|
||||
if (res[m].Valid && gen <= 2 && pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber != gen)
|
||||
pkm.TradebackStatus = TradebackType.WasTradeback;
|
||||
}
|
||||
|
||||
if (gen == generations.Last())
|
||||
{
|
||||
// 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++)
|
||||
{
|
||||
if (res[m]?.Valid ?? false) // Skip valid move
|
||||
continue;
|
||||
if (moves[m] == 0)
|
||||
continue;
|
||||
if (!lvlupegg.Contains(moves[m])) // Check if contains level-up egg moves from parents
|
||||
continue;
|
||||
|
||||
if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1)
|
||||
{
|
||||
res[m] = new CheckResult(Severity.Invalid, V334, CheckIdentifier.Move);
|
||||
MixedGen1NonTradebackGen2 = true;
|
||||
}
|
||||
else
|
||||
res[m] = new CheckResult(Severity.Valid, V345, CheckIdentifier.Move);
|
||||
LvlupEggMovesLearned.Add(m);
|
||||
if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1)
|
||||
pkm.TradebackStatus = TradebackType.WasTradeback;
|
||||
}
|
||||
|
||||
// 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++)
|
||||
{
|
||||
if (res[m]?.Valid ?? false)
|
||||
continue;
|
||||
if (moves[m] == 0)
|
||||
continue;
|
||||
|
||||
if (egg.Contains(moves[m]))
|
||||
{
|
||||
if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1)
|
||||
{
|
||||
// To learn exclusive generation 1 moves the pokemon was tradeback, but it can't be trade to generation 1
|
||||
// without removing moves above MaxMoveID_1, egg moves above MaxMoveID_1 and gen 1 moves are incompatible
|
||||
res[m] = new CheckResult(Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true };
|
||||
MixedGen1NonTradebackGen2 = true;
|
||||
}
|
||||
else
|
||||
res[m] = new CheckResult(Severity.Valid, V171, CheckIdentifier.Move) { Flag = true };
|
||||
|
||||
EggMovesLearned.Add(m);
|
||||
if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1)
|
||||
pkm.TradebackStatus = TradebackType.WasTradeback;
|
||||
}
|
||||
if (!eventegg.Contains(moves[m]))
|
||||
continue;
|
||||
|
||||
if (!egg.Contains(moves[m]))
|
||||
{
|
||||
if (IsGen2Pkm && Gen1MovesLearned.Any() && moves[m] > Legal.MaxMoveID_1)
|
||||
{
|
||||
res[m] = new CheckResult(Severity.Invalid, V334, CheckIdentifier.Move) { Flag = true };
|
||||
MixedGen1NonTradebackGen2 = true;
|
||||
}
|
||||
else
|
||||
res[m] = new CheckResult(Severity.Valid, V333, CheckIdentifier.Move) { Flag = true };
|
||||
}
|
||||
if (pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber == 1)
|
||||
pkm.TradebackStatus = TradebackType.WasTradeback;
|
||||
EventEggMovesLearned.Add(m);
|
||||
}
|
||||
|
||||
// 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
|
||||
var RegularEggMovesLearned = EggMovesLearned.Union(LvlupEggMovesLearned).ToList();
|
||||
if (RegularEggMovesLearned.Any() && EventEggMovesLearned.Any())
|
||||
{
|
||||
// Moves that are egg moves or event egg moves but not both
|
||||
var IncompatibleEggMoves = RegularEggMovesLearned.Except(EventEggMovesLearned).Union(EventEggMovesLearned.Except(RegularEggMovesLearned)).ToList();
|
||||
if (IncompatibleEggMoves.Any())
|
||||
{
|
||||
foreach (int m in IncompatibleEggMoves)
|
||||
{
|
||||
if (EventEggMovesLearned.Contains(m) && !EggMovesLearned.Contains(m))
|
||||
res[m] = new CheckResult(Severity.Invalid, V337, CheckIdentifier.Move);
|
||||
else if (!EventEggMovesLearned.Contains(m) && EggMovesLearned.Contains(m))
|
||||
res[m] = new CheckResult(Severity.Invalid, V336, CheckIdentifier.Move);
|
||||
else if (!EventEggMovesLearned.Contains(m) && LvlupEggMovesLearned.Contains(m))
|
||||
res[m] = new CheckResult(Severity.Invalid, V358, CheckIdentifier.Move);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there is no incompatibility with event egg check that there is no inherited move in gift eggs and event eggs
|
||||
else if (RegularEggMovesLearned.Any() && (pkm.WasGiftEgg || pkm.WasEventEgg))
|
||||
{
|
||||
foreach (int m in RegularEggMovesLearned)
|
||||
{
|
||||
if (EggMovesLearned.Contains(m))
|
||||
res[m] = new CheckResult(Severity.Invalid, pkm.WasGiftEgg ? V377 : V341, CheckIdentifier.Move);
|
||||
else if (LvlupEggMovesLearned.Contains(m))
|
||||
res[m] = new CheckResult(Severity.Invalid, pkm.WasGiftEgg ? V378 : V347, CheckIdentifier.Move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (3 <= gen && gen <= 4 && pkm.Format > gen)
|
||||
{
|
||||
// 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
|
||||
// Check if the marked hidden moves that were invalid at the start are now marked as valid, that means
|
||||
// the hidden move was learned in gen 3 or 4 but was not removed when transfer to 4 or 5
|
||||
if (KnowDefogWhirlpool)
|
||||
{
|
||||
int invalidCount = moves.Where((m, i) => (m == 250 || m == 432) && (res[i]?.Valid ?? false)).Count();
|
||||
if (invalidCount == 2) // can't know both at the same time
|
||||
for (int i = 0; i < 4; i++) // flag both moves
|
||||
if (moves[i] == 250 || moves[i] == 432)
|
||||
res[i] = new CheckResult(Severity.Invalid, V338, CheckIdentifier.Move);
|
||||
}
|
||||
|
||||
for (int i = 0; i < HMLearned.Length; i++)
|
||||
if (res[i]?.Valid ?? false)
|
||||
res[i] = new CheckResult(Severity.Invalid, string.Format(V339, gen, gen + 1), CheckIdentifier.Move);
|
||||
}
|
||||
|
||||
// Mark the gen 1 exclusive moves as illegal because the pokemon also have Non tradeback egg moves.
|
||||
if (MixedGen1NonTradebackGen2)
|
||||
foreach (int m in Gen1MovesLearned)
|
||||
res[m] = new CheckResult(Severity.Invalid, V335, CheckIdentifier.Move);
|
||||
|
||||
if (gen == 1 && pkm.Format == 1 && pkm.Gen1_NotTradeback)
|
||||
{
|
||||
// Check moves learned at the same level in red/blue and yellow, illegal because there is no move reminder
|
||||
// Only two incompatibilites and only there are no illegal combination if generation 2 or 7 are included in the analysis
|
||||
ParseRedYellowIncompatibleMoves(pkm, moves, ref res);
|
||||
|
||||
ParseEvolutionsIncompatibleMoves(pkm, moves, info.EncounterMoves.validTMHMMoves[1], ref res);
|
||||
}
|
||||
|
||||
if (Legal.EvolutionWithMove.Contains(pkm.Species))
|
||||
{
|
||||
// Pokemon that evolved by leveling up while learning a specific move
|
||||
// This pokemon could only have 3 moves from preevolutions that are not the move used to evolved
|
||||
// including special and eggs moves before realearn generations
|
||||
ParseEvolutionLevelupMove(pkm, moves, EggMovesSplitLearned, IncenseMovesLearned, ref res, info);
|
||||
}
|
||||
|
||||
if (res.All(r => r != null))
|
||||
return res;
|
||||
}
|
||||
|
||||
if (pkm.Species == 292 && info.EncounterMatch.Species != 292)
|
||||
{
|
||||
// Ignore Shedinja if the Encounter was also a Shedinja, assume null Encounter as a Nincada egg
|
||||
// Check Shedinja evolved moves from Ninjask after egg moves
|
||||
// Those moves could also be inherited egg moves
|
||||
ParseShedinjaEvolveMoves(pkm, moves, ref res);
|
||||
}
|
||||
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (res[m] == null)
|
||||
res[m] = new CheckResult(Severity.Invalid, V176, CheckIdentifier.Move);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void ParseRedYellowIncompatibleMoves(PKM pkm, int[] moves, ref CheckResult[] res)
|
||||
{
|
||||
var incompatible = new List<int>();
|
||||
if (pkm.Species == 134 && pkm.CurrentLevel < 47 && moves.Contains(151))
|
||||
{
|
||||
// Vaporeon in Yellow learn Mist and Haze at level 42, Mist only if level up in day-care
|
||||
// Vaporeon in Red Blue learn Acid Armor at level 42 and level 47 in Yellow
|
||||
if (moves.Contains(54))
|
||||
incompatible.Add(54);
|
||||
if (moves.Contains(114))
|
||||
incompatible.Add(114);
|
||||
if (incompatible.Any())
|
||||
incompatible.Add(151);
|
||||
}
|
||||
if (pkm.Species == 136 && pkm.CurrentLevel < 47 && moves.Contains(43) && moves.Contains(123))
|
||||
{
|
||||
// Flareon in Yellow learn Smog at level 42
|
||||
// Flareon in Red Blue learn Leer at level 42 and level 47 in Yellow
|
||||
incompatible.Add(43);
|
||||
incompatible.Add(123);
|
||||
}
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (incompatible.Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Invalid, V363, CheckIdentifier.Move);
|
||||
}
|
||||
}
|
||||
private static void ParseEvolutionsIncompatibleMoves(PKM pkm, int[] moves, List<int> tmhm, ref CheckResult[] res)
|
||||
{
|
||||
var species = specieslist;
|
||||
var currentspecies = species[pkm.Species];
|
||||
var previousspecies = string.Empty;
|
||||
var incompatible_previous = new List<int>();
|
||||
var incompatible_current = new List<int>();
|
||||
if (pkm.Species == 34 && moves.Contains(31) && moves.Contains(37))
|
||||
{
|
||||
// Nidoking learns Thrash at level 23
|
||||
// Nidorino learns Fury Attack at level 36, Nidoran♂ at level 30
|
||||
// Other moves are either learned by Nidoran♂ up to level 23 or by TM
|
||||
incompatible_current.Add(31);
|
||||
incompatible_previous.Add(37);
|
||||
previousspecies = species[33];
|
||||
}
|
||||
if (pkm.Species == 103 && moves.Contains(23) && moves.Any(m => Legal.G1Exeggcute_IncompatibleMoves.Contains(moves[m])))
|
||||
{
|
||||
// Exeggutor learns stomp at level 28
|
||||
// Exeggcute learns Stun Spore at 32, PoisonPowder at 37 and Sleep Powder at 48
|
||||
incompatible_current.Add(23);
|
||||
incompatible_previous.AddRange(Legal.G1Exeggcute_IncompatibleMoves);
|
||||
previousspecies = species[103];
|
||||
}
|
||||
if (134 <= pkm.Species && pkm.Species <= 136)
|
||||
{
|
||||
previousspecies = species[133];
|
||||
var ExclusiveMoves = Legal.getExclusiveMoves(133, pkm.Species, 1, tmhm, moves);
|
||||
var EeveeLevels = Legal.getMinLevelLearnMove(133, 1, ExclusiveMoves[0]);
|
||||
var EvoLevels = Legal.getMaxLevelLearnMove(pkm.Species, 1, ExclusiveMoves[1]);
|
||||
|
||||
for (int i = 0; i < ExclusiveMoves[0].Count; i++)
|
||||
{
|
||||
// There is a evolution move with a lower level that current eevee move
|
||||
if (EvoLevels.Any(ev => ev < EeveeLevels[i]))
|
||||
incompatible_previous.Add(ExclusiveMoves[0][i]);
|
||||
}
|
||||
for (int i = 0; i < ExclusiveMoves[1].Count; i++)
|
||||
{
|
||||
// There is a eevee move with a greather level that current evolution move
|
||||
if (EeveeLevels.Any(ev => ev > EvoLevels[i]))
|
||||
incompatible_current.Add(ExclusiveMoves[1][i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (incompatible_current.Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Invalid, string.Format(V365, currentspecies, previousspecies), CheckIdentifier.Move);
|
||||
if (incompatible_previous.Contains(moves[m]))
|
||||
res[m] = new CheckResult(Severity.Invalid, string.Format(V366, currentspecies, previousspecies), CheckIdentifier.Move);
|
||||
}
|
||||
}
|
||||
private static void ParseShedinjaEvolveMoves(PKM pkm, int[] moves, ref CheckResult[] res)
|
||||
{
|
||||
List<int>[] ShedinjaEvoMoves = Legal.getShedinjaEvolveMoves(pkm);
|
||||
var ShedinjaEvoMovesLearned = new List<int>();
|
||||
for (int gen = Math.Min(pkm.Format, 4); gen >= 3; gen--)
|
||||
{
|
||||
bool native = gen == pkm.Format;
|
||||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (res[m]?.Valid ?? false)
|
||||
continue;
|
||||
|
||||
if (!ShedinjaEvoMoves[gen].Contains(moves[m]))
|
||||
continue;
|
||||
|
||||
res[m] = new CheckResult(Severity.Valid, native ? V355 : string.Format(V356, gen), CheckIdentifier.Move);
|
||||
ShedinjaEvoMovesLearned.Add(m);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShedinjaEvoMovesLearned.Count <= 1)
|
||||
return;
|
||||
|
||||
foreach (int m in ShedinjaEvoMovesLearned)
|
||||
res[m] = new CheckResult(Severity.Invalid, V357, CheckIdentifier.Move);
|
||||
}
|
||||
private static void ParseEvolutionLevelupMove(PKM pkm, int[] moves, List<int>[] EggMovesSplitLearned, List<int> IncenseMovesLearned, ref CheckResult[] res, LegalInfo info)
|
||||
{
|
||||
// Ignore if there is an invalid move or an empty move, this validation is only for 4 non-empty moves that are all valid, but invalid as a 4 combination
|
||||
// Ignore Mr. Mime and Sudowodoo from generations 1 to 3, they cant be evolved from Bonsly or Munchlax
|
||||
// Ignore if encounter species is the evolution species, the pokemon was not evolved by the player
|
||||
if (!res.All(r => r?.Valid ?? false) || moves.Any(m => m == 0) ||
|
||||
(Legal.BabyEvolutionWithMove.Contains(pkm.Species) && pkm.GenNumber <= 3) ||
|
||||
info.EncounterMatch.Species == pkm.Species)
|
||||
return;
|
||||
|
||||
// Mr.Mime and Sodowodoo from eggs that does not have any exclusive egg move or level up move from Mime Jr or Bonsly.
|
||||
// The egg can be assumed to be a non-incense egg if the pokemon was not evolved by the player
|
||||
if (info.EncounterMatch.EggEncounter && Legal.BabyEvolutionWithMove.Contains(pkm.Species) &&
|
||||
!IncenseMovesLearned.Any() && !EggMovesSplitLearned[0].Any())
|
||||
return;
|
||||
|
||||
var ValidMoves = Legal.getValidPostEvolutionMoves(pkm, pkm.Species, info.EvoChainsAllGens, GameVersion.Any);
|
||||
// Add the evolution moves to valid moves in case some of this moves could not be learned after evolving
|
||||
switch (pkm.Species)
|
||||
{
|
||||
case 122: // Mr. Mime (Mime Jr with Mimic)
|
||||
case 185: // Sudowoodo (Bonsly with Mimic)
|
||||
ValidMoves.Add(102);
|
||||
break;
|
||||
case 424: // Ambipom (Aipom with Double Hit)
|
||||
ValidMoves.Add(458);
|
||||
break;
|
||||
case 463: // Lickilicky (Lickitung with Rollout)
|
||||
ValidMoves.Add(205);
|
||||
break;
|
||||
case 465: // Tangrowth (Tangela with Ancient Power)
|
||||
case 469: // Yanmega (Yamma with Ancient Power)
|
||||
case 473: // Mamoswine (Piloswine with Ancient Power)
|
||||
ValidMoves.Add(246);
|
||||
break;
|
||||
case 700: // Sylveon (Eevee with Fairy Move)
|
||||
// Add every fairy moves without cheking if eevee learn it or not, pokemon moves are determined legal before this function
|
||||
ValidMoves.AddRange(Legal.FairyMoves);
|
||||
break;
|
||||
case 763: // Tsareena (Steenee with Stomp)
|
||||
ValidMoves.Add(023);
|
||||
break;
|
||||
}
|
||||
|
||||
if (moves.Any(m => ValidMoves.Contains(m)))
|
||||
return;
|
||||
|
||||
for (int m = 0; m < 4; m++)
|
||||
res[m] = new CheckResult(Severity.Invalid, string.Format(V385, specieslist[pkm.Species]), CheckIdentifier.Move);
|
||||
}
|
||||
|
||||
/* Similar to verifyRelearnEgg but in pre relearn generation is the moves what should match the expected order but only if the pokemon is inside an egg */
|
||||
private static CheckResult[] verifyPreRelearnEggBase(PKM pkm, int[] Moves, List<int> baseMoves, List<int> eggmoves, List<int> lvlmoves, List<int> tmhmmoves, List<int> tutormoves, List<int> specialmoves, bool AllowInherited, GameVersion ver)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
|
||||
// Obtain level1 moves
|
||||
int baseCt = baseMoves.Count;
|
||||
if (baseCt > 4) baseCt = 4;
|
||||
|
||||
// Obtain Inherited moves
|
||||
var inherited = Moves.Where(m => m != 0 && (!baseMoves.Contains(m) || specialmoves.Contains(m) || eggmoves.Contains(m) || lvlmoves.Contains(m) || tmhmmoves.Contains(m) || tutormoves.Contains(m))).ToList();
|
||||
int inheritCt = inherited.Count;
|
||||
|
||||
// Get required amount of base moves
|
||||
int unique = baseMoves.Concat(inherited).Distinct().Count();
|
||||
int reqBase = inheritCt == 4 || baseCt + inheritCt > 4 ? 4 - inheritCt : baseCt;
|
||||
if (Moves.Where(m => m != 0).Count() < Math.Min(4, baseMoves.Count))
|
||||
reqBase = Math.Min(4, unique);
|
||||
|
||||
var em = string.Empty;
|
||||
var moveoffset = 0;
|
||||
// Check if the required amount of Base Egg Moves are present.
|
||||
for (int i = moveoffset; i < reqBase; i++)
|
||||
{
|
||||
if (baseMoves.Contains(Moves[i]))
|
||||
res[i] = new CheckResult(Severity.Valid, V179, CheckIdentifier.Move);
|
||||
else
|
||||
{
|
||||
// mark remaining base egg moves missing
|
||||
for (int z = i; z < reqBase; z++)
|
||||
res[z] = new CheckResult(Severity.Invalid, V180, CheckIdentifier.Move);
|
||||
|
||||
// provide the list of suggested base moves for the last required slot
|
||||
em = string.Join(", ", baseMoves.Select(m => m >= movelist.Length ? V190 : movelist[m]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
moveoffset += reqBase;
|
||||
|
||||
// Check also if the required amount of Special Egg Moves are present, ir are after base moves
|
||||
for (int i = moveoffset; i < moveoffset + specialmoves.Count; i++)
|
||||
{
|
||||
if (specialmoves.Contains(Moves[i]))
|
||||
res[i] = new CheckResult(Severity.Valid, V333, CheckIdentifier.Move);
|
||||
else
|
||||
{
|
||||
// mark remaining special egg moves missing
|
||||
for (int z = i; z < moveoffset + specialmoves.Count; z++)
|
||||
res[z] = new CheckResult(Severity.Invalid, V342, CheckIdentifier.Move);
|
||||
|
||||
// provide the list of suggested base moves and species moves for the last required slot
|
||||
if (!string.IsNullOrEmpty(em)) em += ", ";
|
||||
else
|
||||
em = string.Join(", ", baseMoves.Select(m => m >= movelist.Length ? V190 : movelist[m])) + ", ";
|
||||
em += string.Join(", ", specialmoves.Select(m => m >= movelist.Length ? V190 : movelist[m]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(em))
|
||||
res[reqBase > 0 ? reqBase - 1 : 0].Comment = string.Format(Environment.NewLine + V343, em);
|
||||
// Non-Base moves that can magically appear in the regular movepool
|
||||
if (pkm.GenNumber >= 3 && Legal.LightBall.Contains(pkm.Species))
|
||||
eggmoves.Add(344);
|
||||
|
||||
// Inherited moves appear after the required base moves.
|
||||
var AllowInheritedSeverity = AllowInherited ? Severity.Valid : Severity.Invalid;
|
||||
for (int i = reqBase + specialmoves.Count; i < 4; i++)
|
||||
{
|
||||
if (Moves[i] == 0) // empty
|
||||
res[i] = new CheckResult(Severity.Valid, V167, CheckIdentifier.Move);
|
||||
else if (eggmoves.Contains(Moves[i])) // inherited egg move
|
||||
res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V344 : V341, CheckIdentifier.Move);
|
||||
else if (lvlmoves.Contains(Moves[i])) // inherited lvl moves
|
||||
res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V345 : V347, CheckIdentifier.Move);
|
||||
else if (tmhmmoves.Contains(Moves[i])) // inherited TMHM moves
|
||||
res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V349 : V350, CheckIdentifier.Move);
|
||||
else if (tutormoves.Contains(Moves[i])) // inherited tutor moves
|
||||
res[i] = new CheckResult(AllowInheritedSeverity, AllowInherited ? V346 : V348, CheckIdentifier.Move);
|
||||
else // not inheritable, flag
|
||||
res[i] = new CheckResult(Severity.Invalid, V340, CheckIdentifier.Move);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void verifyNoEmptyDuplicates(int[] Moves, CheckResult[] res)
|
||||
{
|
||||
bool emptySlot = false;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (Moves[i] == 0)
|
||||
emptySlot = true;
|
||||
else if (emptySlot)
|
||||
res[i] = new CheckResult(Severity.Invalid, V167, res[i].Identifier);
|
||||
else if (Moves.Count(m => m == Moves[i]) > 1)
|
||||
res[i] = new CheckResult(Severity.Invalid, V168, res[i].Identifier);
|
||||
}
|
||||
}
|
||||
private static void UptateGen1LevelUpMoves(PKM pkm, ValidEncounterMoves EncounterMoves, int defaultLvlG1, int generation, LegalInfo info)
|
||||
{
|
||||
switch (generation)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
var lvlG1 = info.EncounterMatch?.LevelMin + 1 ?? 6;
|
||||
if (lvlG1 != defaultLvlG1)
|
||||
EncounterMoves.validLevelUpMoves[1] = Legal.getValidMoves(pkm, info.EvoChainsAllGens[1], generation: 1, minLvLG1: lvlG1, LVL: true, Tutor: false, Machine: false, MoveReminder: false).ToList();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
private static int[] getGenMovesCheckOrder(PKM pkm)
|
||||
{
|
||||
if (pkm.Format == 1)
|
||||
return new[] { 1, 2 };
|
||||
if (pkm.Format == 2)
|
||||
return new[] { 2, 1 };
|
||||
if (pkm.Format == 7 && pkm.VC1)
|
||||
return new[] { 7, 1 };
|
||||
if (pkm.Format == 7 && pkm.VC2)
|
||||
return new[] { 7, 2, 1 };
|
||||
|
||||
var order = new int[pkm.Format - pkm.GenNumber + 1];
|
||||
for (int i = 0; i < order.Length; i++)
|
||||
order[i] = pkm.Format - i;
|
||||
return order;
|
||||
}
|
||||
private static ValidEncounterMoves getEncounterValidMoves(PKM pkm, LegalInfo info)
|
||||
{
|
||||
var minLvLG1 = pkm.GenNumber <= 2 ? info.EncounterMatch.LevelMin + 1 : 0;
|
||||
var encounterspecies = info.EncounterMatch.Species;
|
||||
var EvoChainsAllGens = info.EvoChainsAllGens;
|
||||
// If encounter species is the same species from the first match, the one in variable EncounterMatch, its evolution chains is already in EvoChainsAllGens
|
||||
var LevelMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, minLvLG1: minLvLG1, Tutor: false, Machine: false, RemoveTransferHM: false);
|
||||
var TMHMMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Tutor: false, MoveReminder: false, RemoveTransferHM: false);
|
||||
var TutorMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Machine: false, MoveReminder: false, RemoveTransferHM: false);
|
||||
return new ValidEncounterMoves
|
||||
{
|
||||
EncounterSpecies = encounterspecies,
|
||||
validLevelUpMoves = LevelMoves,
|
||||
validTMHMMoves = TMHMMoves,
|
||||
validTutorMoves = TutorMoves,
|
||||
EvolutionChains = EvoChainsAllGens,
|
||||
minLvlG1 = minLvLG1
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
267
PKHeX.Core/Legality/Encounters/VerifyEncounter.cs
Normal file
267
PKHeX.Core/Legality/Encounters/VerifyEncounter.cs
Normal file
|
@ -0,0 +1,267 @@
|
|||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class VerifyEncounter
|
||||
{
|
||||
public static CheckResult verifyEncounter(PKM pkm, IEncounterable encounter)
|
||||
{
|
||||
if (encounter is EncounterEgg e)
|
||||
{
|
||||
pkm.WasEgg = true;
|
||||
if (pkm.GenNumber == 3)
|
||||
return verifyEncounterEgg3Transfer(pkm);
|
||||
return verifyEncounterEgg(pkm, e);
|
||||
}
|
||||
if (encounter is EncounterLink l)
|
||||
return verifyEncounterLink(pkm, l);
|
||||
if (encounter is EncounterTrade t)
|
||||
return verifyEncounterTrade(pkm, t);
|
||||
if (encounter is EncounterSlot w)
|
||||
return verifyEncounterWild(pkm, w);
|
||||
if (encounter is EncounterStatic s)
|
||||
return verifyEncounterStatic(pkm, s, null);
|
||||
if (encounter is MysteryGift g)
|
||||
return verifyEncounterEvent(pkm, g);
|
||||
|
||||
return new CheckResult(Severity.Invalid, V80, CheckIdentifier.Encounter);
|
||||
}
|
||||
public static CheckResult verifyEncounterG12(PKM pkm, IEncounterable encounter)
|
||||
{
|
||||
var EncounterMatch = encounter is GBEncounterData g ? g.Encounter : encounter;
|
||||
if (encounter.EggEncounter)
|
||||
{
|
||||
pkm.WasEgg = true;
|
||||
return verifyEncounterEgg(pkm, EncounterMatch);
|
||||
}
|
||||
if (EncounterMatch is EncounterSlot)
|
||||
return new CheckResult(Severity.Valid, V68, CheckIdentifier.Encounter);
|
||||
if (EncounterMatch is EncounterStatic s)
|
||||
return verifyEncounterStatic(pkm, s, null);
|
||||
if (EncounterMatch is EncounterTrade t)
|
||||
return verifyEncounterTrade(pkm, t);
|
||||
|
||||
return new CheckResult(Severity.Invalid, V80, CheckIdentifier.Encounter);
|
||||
}
|
||||
|
||||
// Eggs
|
||||
private static CheckResult verifyEncounterEgg(PKM pkm, IEncounterable egg)
|
||||
{
|
||||
// Check Species
|
||||
if (Legal.NoHatchFromEgg.Contains(pkm.Species))
|
||||
return new CheckResult(Severity.Invalid, V50, CheckIdentifier.Encounter);
|
||||
switch (pkm.GenNumber)
|
||||
{
|
||||
case 1:
|
||||
case 2: return new CheckResult(CheckIdentifier.Encounter); // no met location info
|
||||
case 3: return verifyEncounterEgg3(pkm);
|
||||
case 4: return pkm.IsEgg ? verifyUnhatchedEgg(pkm, 02002) : verifyEncounterEgg4(pkm);
|
||||
case 5: return pkm.IsEgg ? verifyUnhatchedEgg(pkm, 30002) : verifyEncounterEgg5(pkm);
|
||||
case 6: return pkm.IsEgg ? verifyUnhatchedEgg(pkm, 30002) : verifyEncounterEgg6(pkm);
|
||||
case 7: return pkm.IsEgg ? verifyUnhatchedEgg(pkm, 30002) : verifyEncounterEgg7(pkm);
|
||||
|
||||
default: // none of the above
|
||||
return new CheckResult(Severity.Invalid, V51, CheckIdentifier.Encounter);
|
||||
}
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg3(PKM pkm)
|
||||
{
|
||||
return pkm.Format == 3 ? verifyEncounterEgg3Native(pkm) : verifyEncounterEgg3Transfer(pkm);
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg3Native(PKM pkm)
|
||||
{
|
||||
if (pkm.Met_Level != 0)
|
||||
return new CheckResult(Severity.Invalid, string.Format(V52, 0), CheckIdentifier.Encounter);
|
||||
if (pkm.IsEgg)
|
||||
{
|
||||
var loc = pkm.FRLG ? Legal.ValidEggMet_FRLG : Legal.ValidEggMet_RSE;
|
||||
if (!loc.Contains(pkm.Met_Location))
|
||||
return new CheckResult(Severity.Invalid, V55, CheckIdentifier.Encounter);
|
||||
}
|
||||
else
|
||||
{
|
||||
var locs = pkm.FRLG ? Legal.ValidMet_FRLG : pkm.E ? Legal.ValidMet_E : Legal.ValidMet_RS;
|
||||
if (locs.Contains(pkm.Met_Location))
|
||||
return new CheckResult(Severity.Valid, V53, CheckIdentifier.Encounter);
|
||||
if (Legal.ValidMet_FRLG.Contains(pkm.Met_Location) || Legal.ValidMet_E.Contains(pkm.Met_Location) || Legal.ValidMet_RS.Contains(pkm.Met_Location))
|
||||
return new CheckResult(Severity.Valid, V56, CheckIdentifier.Encounter);
|
||||
return new CheckResult(Severity.Invalid, V54, CheckIdentifier.Encounter);
|
||||
}
|
||||
return new CheckResult(Severity.Valid, V53, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg3Transfer(PKM pkm)
|
||||
{
|
||||
if (pkm.IsEgg)
|
||||
return new CheckResult(Severity.Invalid, V57, CheckIdentifier.Encounter);
|
||||
if (pkm.Met_Level < 5)
|
||||
return new CheckResult(Severity.Invalid, V58, CheckIdentifier.Encounter);
|
||||
if (pkm.Egg_Location != 0)
|
||||
return new CheckResult(Severity.Invalid, V59, CheckIdentifier.Encounter);
|
||||
if (pkm.Format == 4 && pkm.Met_Location != 0x37) // Pal Park
|
||||
return new CheckResult(Severity.Invalid, V60, CheckIdentifier.Encounter);
|
||||
if (pkm.Format != 4 && pkm.Met_Location != 30001)
|
||||
return new CheckResult(Severity.Invalid, V61, CheckIdentifier.Encounter);
|
||||
|
||||
return new CheckResult(Severity.Valid, V53, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg4(PKM pkm)
|
||||
{
|
||||
if (pkm.Format == 4)
|
||||
return verifyEncounterEggLevelLoc(pkm, 0, pkm.HGSS ? Legal.ValidMet_HGSS : pkm.Pt ? Legal.ValidMet_Pt : Legal.ValidMet_DP);
|
||||
if (pkm.IsEgg)
|
||||
return new CheckResult(Severity.Invalid, V57, CheckIdentifier.Encounter);
|
||||
// transferred
|
||||
if (pkm.Met_Level < 1)
|
||||
return new CheckResult(Severity.Invalid, V58, CheckIdentifier.Encounter);
|
||||
|
||||
if (pkm.Met_Location != 30001)
|
||||
return new CheckResult(Severity.Invalid, V61, CheckIdentifier.Encounter);
|
||||
return new CheckResult(Severity.Valid, V53, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg5(PKM pkm)
|
||||
{
|
||||
return verifyEncounterEggLevelLoc(pkm, 1, pkm.B2W2 ? Legal.ValidMet_B2W2 : Legal.ValidMet_BW);
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg6(PKM pkm)
|
||||
{
|
||||
if (pkm.AO)
|
||||
return verifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_AO);
|
||||
|
||||
if (pkm.Egg_Location == 318)
|
||||
return new CheckResult(Severity.Invalid, V55, CheckIdentifier.Encounter);
|
||||
|
||||
return verifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_XY);
|
||||
}
|
||||
private static CheckResult verifyEncounterEgg7(PKM pkm)
|
||||
{
|
||||
if (pkm.SM)
|
||||
return verifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_SM);
|
||||
|
||||
// no other games
|
||||
return new CheckResult(Severity.Invalid, V51, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterEggLevelLoc(PKM pkm, int eggLevel, int[] MetLocations)
|
||||
{
|
||||
if (pkm.Met_Level != eggLevel)
|
||||
return new CheckResult(Severity.Invalid, string.Format(V52, eggLevel), CheckIdentifier.Encounter);
|
||||
return MetLocations.Contains(pkm.Met_Location)
|
||||
? new CheckResult(Severity.Valid, V53, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Invalid, V54, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyUnhatchedEgg(PKM pkm, int tradeLoc)
|
||||
{
|
||||
if (pkm.Egg_Location == tradeLoc)
|
||||
return new CheckResult(Severity.Invalid, V62, CheckIdentifier.Encounter);
|
||||
|
||||
if (pkm.Met_Location == tradeLoc)
|
||||
return new CheckResult(Severity.Valid, V56, CheckIdentifier.Encounter);
|
||||
return pkm.Met_Location == 0
|
||||
? new CheckResult(Severity.Valid, V63, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Invalid, V59, CheckIdentifier.Encounter);
|
||||
}
|
||||
|
||||
// Etc
|
||||
private static CheckResult verifyEncounterWild(PKM pkm, EncounterSlot slot)
|
||||
{
|
||||
// Check for Unreleased Encounters / Collisions
|
||||
switch (pkm.GenNumber)
|
||||
{
|
||||
case 4:
|
||||
if (pkm.HasOriginalMetLocation && pkm.Met_Location == 193 && slot.Type == SlotType.Surf)
|
||||
{
|
||||
// Pokemon surfing in Johto Route 45
|
||||
return new CheckResult(Severity.Invalid, V384, CheckIdentifier.Encounter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (slot.Normal)
|
||||
return slot.Pressure
|
||||
? new CheckResult(Severity.Valid, V67, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Valid, V68, CheckIdentifier.Encounter);
|
||||
|
||||
// Decreased Level Encounters
|
||||
if (slot.WhiteFlute)
|
||||
return slot.Pressure
|
||||
? new CheckResult(Severity.Valid, V69, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Valid, V70, CheckIdentifier.Encounter);
|
||||
|
||||
// Increased Level Encounters
|
||||
if (slot.BlackFlute)
|
||||
return slot.Pressure
|
||||
? new CheckResult(Severity.Valid, V71, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Valid, V72, CheckIdentifier.Encounter);
|
||||
|
||||
if (slot.Pressure)
|
||||
return new CheckResult(Severity.Valid, V67, CheckIdentifier.Encounter);
|
||||
|
||||
return new CheckResult(Severity.Valid, V73, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterStatic(PKM pkm, EncounterStatic s, CheckResult[] vRelearn)
|
||||
{
|
||||
// Check for Unreleased Encounters / Collisions
|
||||
switch (pkm.GenNumber)
|
||||
{
|
||||
case 3:
|
||||
if (s is EncounterStaticShadow w && w.EReader && pkm.Language != 1) // Non-JP E-reader Pokemon
|
||||
return new CheckResult(Severity.Invalid, V406, CheckIdentifier.Encounter);
|
||||
if (pkm.Species == 151 && s.Location == 201 && pkm.Language != 1) // Non-JP Mew (Old Sea Map)
|
||||
return new CheckResult(Severity.Invalid, V353, CheckIdentifier.Encounter);
|
||||
break;
|
||||
case 4:
|
||||
if (pkm.Species == 493 && s.Location == 086) // Azure Flute Arceus
|
||||
return new CheckResult(Severity.Invalid, V352, CheckIdentifier.Encounter);
|
||||
if (pkm.Species == 491 && s.Location == 079 && !pkm.Pt) // DP Darkrai
|
||||
return new CheckResult(Severity.Invalid, V383, CheckIdentifier.Encounter);
|
||||
if (pkm.Species == 492 && s.Location == 063 && !pkm.Pt) // DP Shaymin
|
||||
return new CheckResult(Severity.Invalid, V354, CheckIdentifier.Encounter);
|
||||
if (s.Location == 193 && (s as EncounterStaticTyped)?.TypeEncounter == EncounterType.Surfing_Fishing) // Roaming pokemon surfin in Johto Route 45
|
||||
return new CheckResult(Severity.Invalid, V384, CheckIdentifier.Encounter);
|
||||
break;
|
||||
case 7:
|
||||
if (s.EggLocation == 60002 && vRelearn.All(rl => rl.Valid))
|
||||
return new CheckResult(Severity.Invalid, V74, CheckIdentifier.RelearnMove); // not gift egg
|
||||
break;
|
||||
}
|
||||
|
||||
return new CheckResult(Severity.Valid, V75, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterTrade(PKM pkm, EncounterTrade trade)
|
||||
{
|
||||
if (trade.Species == pkm.Species && trade.EvolveOnTrade)
|
||||
{
|
||||
// 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 = LegalityAnalysis.specieslist;
|
||||
var unevolved = species[pkm.Species];
|
||||
var evolved = species[pkm.Species + 1];
|
||||
return new CheckResult(Severity.Invalid, string.Format(V401, unevolved, evolved), CheckIdentifier.Encounter);
|
||||
}
|
||||
return new CheckResult(Severity.Valid, V76, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterLink(PKM pkm, EncounterLink enc)
|
||||
{
|
||||
// Should NOT be Fateful, and should be in Database
|
||||
if (enc == null)
|
||||
return new CheckResult(Severity.Invalid, V43, CheckIdentifier.Encounter);
|
||||
|
||||
if (pkm.XY && !enc.XY)
|
||||
return new CheckResult(Severity.Invalid, V44, CheckIdentifier.Encounter);
|
||||
if (pkm.AO && !enc.ORAS)
|
||||
return new CheckResult(Severity.Invalid, V45, CheckIdentifier.Encounter);
|
||||
|
||||
if (enc.Shiny != null && (bool)enc.Shiny ^ pkm.IsShiny)
|
||||
return new CheckResult(Severity.Invalid, V47, CheckIdentifier.Encounter);
|
||||
|
||||
return pkm.FatefulEncounter
|
||||
? new CheckResult(Severity.Invalid, V48, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Valid, V49, CheckIdentifier.Encounter);
|
||||
}
|
||||
private static CheckResult verifyEncounterEvent(PKM pkm, MysteryGift MatchedGift)
|
||||
{
|
||||
// Strict matching already performed by EncounterGenerator. May be worth moving some checks here to better flag invalid gifts.
|
||||
return new CheckResult(Severity.Valid, string.Format(V21, MatchedGift.CardID.ToString("0000"), MatchedGift.CardTitle), CheckIdentifier.Encounter);
|
||||
}
|
||||
}
|
||||
}
|
31
PKHeX.Core/Legality/Encounters/VerifyEvolution.cs
Normal file
31
PKHeX.Core/Legality/Encounters/VerifyEvolution.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class VerifyEvolution
|
||||
{
|
||||
// Evolutions
|
||||
public static CheckResult verifyEvolution(PKM pkm, IEncounterable EncounterMatch)
|
||||
{
|
||||
if (!isValidEvolution(pkm, EncounterMatch))
|
||||
return new CheckResult(Severity.Invalid, V86, CheckIdentifier.Evolution);
|
||||
return new CheckResult(CheckIdentifier.Evolution);
|
||||
}
|
||||
private static bool isValidEvolution(PKM pkm, IEncounterable EncounterMatch)
|
||||
{
|
||||
if (pkm.WasEgg && !Legal.getEvolutionValid(pkm) && pkm.Species != 350)
|
||||
return false;
|
||||
|
||||
if (EncounterMatch.Species == pkm.Species)
|
||||
return true;
|
||||
|
||||
var matchEvo = Legal.getValidPreEvolutions(pkm).FirstOrDefault(z => z.Species == EncounterMatch.Species);
|
||||
if (matchEvo == null)
|
||||
return false;
|
||||
return matchEvo.RequiresLvlUp
|
||||
? matchEvo.Level > EncounterMatch.LevelMin
|
||||
: matchEvo.Level >= EncounterMatch.LevelMin;
|
||||
}
|
||||
}
|
||||
}
|
184
PKHeX.Core/Legality/Encounters/VerifyRelearnMoves.cs
Normal file
184
PKHeX.Core/Legality/Encounters/VerifyRelearnMoves.cs
Normal file
|
@ -0,0 +1,184 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityAnalysis;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public static class VerifyRelearnMoves
|
||||
{
|
||||
public static CheckResult[] verifyRelearn(PKM pkm, LegalInfo info)
|
||||
{
|
||||
if (pkm.GenNumber < 6 || pkm.VC1)
|
||||
return verifyRelearnNone(pkm, info);
|
||||
|
||||
if (info.EncounterMatch is EncounterLink l)
|
||||
return verifyRelearnLink(pkm, l, info);
|
||||
|
||||
if (info.EncounterMatch is MysteryGift g)
|
||||
return verifyRelearnMysteryGift(pkm, g, info);
|
||||
|
||||
if (info.EncounterMatch is EncounterEgg e)
|
||||
return verifyRelearnEgg(pkm, e, info);
|
||||
|
||||
if (pkm.RelearnMove1 != 0 && info.EncounterMatch is EncounterSlot z && z.DexNav && EncounterGenerator.getDexNavValid(pkm))
|
||||
return verifyRelearnDexNav(pkm, info);
|
||||
|
||||
return verifyRelearnNone(pkm, info);
|
||||
}
|
||||
|
||||
private static CheckResult[] verifyRelearnMysteryGift(PKM pkm, MysteryGift mg, LegalInfo info)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
int[] RelearnMoves = pkm.RelearnMoves;
|
||||
// Get gifts that match
|
||||
|
||||
int[] moves = mg.RelearnMoves;
|
||||
for (int i = 0; i < 4; i++)
|
||||
res[i] = moves[i] != RelearnMoves[i]
|
||||
? new CheckResult(Severity.Invalid, string.Format(V178, movelist[moves[i]]), CheckIdentifier.RelearnMove)
|
||||
: new CheckResult(CheckIdentifier.RelearnMove);
|
||||
|
||||
info.RelearnBase = moves;
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] verifyRelearnDexNav(PKM pkm, LegalInfo info)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
int[] RelearnMoves = pkm.RelearnMoves;
|
||||
|
||||
// DexNav Pokémon can have 1 random egg move as a relearn move.
|
||||
res[0] = !Legal.getValidRelearn(pkm, 0).Contains(RelearnMoves[0])
|
||||
? new CheckResult(Severity.Invalid, V183, CheckIdentifier.RelearnMove)
|
||||
: new CheckResult(CheckIdentifier.RelearnMove);
|
||||
|
||||
// All other relearn moves must be empty.
|
||||
for (int i = 1; i < 4; i++)
|
||||
res[i] = RelearnMoves[i] != 0
|
||||
? new CheckResult(Severity.Invalid, V184, CheckIdentifier.RelearnMove)
|
||||
: new CheckResult(CheckIdentifier.RelearnMove);
|
||||
|
||||
// Update the relearn base moves if the first relearn move is okay.
|
||||
info.RelearnBase = res[0].Valid
|
||||
? RelearnMoves
|
||||
: new int[4];
|
||||
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] verifyRelearnNone(PKM pkm, LegalInfo info)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
int[] RelearnMoves = pkm.RelearnMoves;
|
||||
info.RelearnBase = new int[4];
|
||||
|
||||
// No relearn moves should be present.
|
||||
for (int i = 0; i < 4; i++)
|
||||
res[i] = RelearnMoves[i] != 0
|
||||
? new CheckResult(Severity.Invalid, V184, CheckIdentifier.RelearnMove)
|
||||
: new CheckResult(CheckIdentifier.RelearnMove);
|
||||
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] verifyRelearnLink(PKM pkm, EncounterLink l, LegalInfo info)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
int[] RelearnMoves = pkm.RelearnMoves;
|
||||
int[] LinkRelearn = l.RelearnMoves;
|
||||
|
||||
// Pokémon Link encounters should have their relearn moves match exactly.
|
||||
info.RelearnBase = LinkRelearn;
|
||||
for (int i = 0; i < 4; i++)
|
||||
res[i] = LinkRelearn[i] != RelearnMoves[i]
|
||||
? new CheckResult(Severity.Invalid, string.Format(V178, movelist[LinkRelearn[i]]), CheckIdentifier.RelearnMove)
|
||||
: new CheckResult(CheckIdentifier.RelearnMove);
|
||||
|
||||
return res;
|
||||
}
|
||||
private static CheckResult[] verifyRelearnEgg(PKM pkm, EncounterEgg e, LegalInfo info)
|
||||
{
|
||||
int[] RelearnMoves = pkm.RelearnMoves;
|
||||
info.RelearnBase = new int[4];
|
||||
|
||||
// Generate & Analyze compatibility
|
||||
return verifyRelearnEggBase(pkm, info, e, RelearnMoves);
|
||||
}
|
||||
private static CheckResult[] verifyRelearnEggBase(PKM pkm, LegalInfo info, EncounterEgg e, int[] RelearnMoves)
|
||||
{
|
||||
CheckResult[] res = new CheckResult[4];
|
||||
|
||||
// Obtain level1 moves
|
||||
List<int> baseMoves = new List<int>(Legal.getBaseEggMoves(pkm, e.Species, e.Game, 1));
|
||||
int baseCt = baseMoves.Count;
|
||||
if (baseCt > 4) baseCt = 4;
|
||||
|
||||
// Obtain Inherited moves
|
||||
var inheritMoves = Legal.getValidRelearn(pkm, e.Species).ToList();
|
||||
var inherited = RelearnMoves.Where(m => m != 0 && (!baseMoves.Contains(m) || inheritMoves.Contains(m))).ToList();
|
||||
int inheritCt = inherited.Count;
|
||||
|
||||
|
||||
// Get required amount of base moves
|
||||
int unique = baseMoves.Concat(inherited).Distinct().Count();
|
||||
int reqBase = inheritCt == 4 || baseCt + inheritCt > 4 ? 4 - inheritCt : baseCt;
|
||||
if (RelearnMoves.Where(m => m != 0).Count() < Math.Min(4, baseMoves.Count))
|
||||
reqBase = Math.Min(4, unique);
|
||||
|
||||
// Check if the required amount of Base Egg Moves are present.
|
||||
for (int i = 0; i < reqBase; i++)
|
||||
{
|
||||
if (baseMoves.Contains(RelearnMoves[i]))
|
||||
res[i] = new CheckResult(Severity.Valid, V179, CheckIdentifier.RelearnMove);
|
||||
else
|
||||
{
|
||||
// mark remaining base egg moves missing
|
||||
for (int z = i; z < reqBase; z++)
|
||||
res[z] = new CheckResult(Severity.Invalid, V180, CheckIdentifier.RelearnMove);
|
||||
|
||||
// provide the list of suggested base moves for the last required slot
|
||||
string em = string.Join(", ", baseMoves.Select(m => m >= movelist.Length ? V190 : movelist[m]));
|
||||
res[reqBase - 1].Comment += string.Format(Environment.NewLine + V181, em);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-Base moves that can magically appear in the regular movepool
|
||||
if (Legal.LightBall.Contains(pkm.Species))
|
||||
inheritMoves.Add(344);
|
||||
|
||||
// Begin verification of moves
|
||||
|
||||
// If any splitbreed moves are invalid, flag accordingly
|
||||
var splitInvalid = false;
|
||||
var splitMoves = e.SplitBreed ? Legal.getValidRelearn(pkm, Legal.getBaseEggSpecies(pkm)).ToList() : new List<int>();
|
||||
|
||||
// Inherited moves appear after the required base moves.
|
||||
for (int i = reqBase; i < 4; i++)
|
||||
{
|
||||
if (RelearnMoves[i] == 0) // empty
|
||||
res[i] = new CheckResult(Severity.Valid, V167, CheckIdentifier.RelearnMove);
|
||||
else if (inheritMoves.Contains(RelearnMoves[i])) // inherited
|
||||
res[i] = new CheckResult(Severity.Valid, V172, CheckIdentifier.RelearnMove);
|
||||
else if (e.SplitBreed && splitMoves.Contains(RelearnMoves[i])) // inherited
|
||||
splitInvalid = true;
|
||||
else // not inheritable, flag
|
||||
res[i] = new CheckResult(Severity.Invalid, V182, CheckIdentifier.RelearnMove);
|
||||
}
|
||||
|
||||
if (splitInvalid)
|
||||
{
|
||||
var splitSpecies = Legal.getBaseEggSpecies(pkm);
|
||||
for (int i = reqBase; i < 4; i++)
|
||||
{
|
||||
if (inheritMoves.Contains(RelearnMoves[i]) && !splitMoves.Contains(RelearnMoves[i]))
|
||||
res[i] = new CheckResult(Severity.Invalid, string.Format(V379, specieslist[splitSpecies], specieslist[e.Species]), CheckIdentifier.RelearnMove);
|
||||
if (!inheritMoves.Contains(RelearnMoves[i]) && splitMoves.Contains(RelearnMoves[i]))
|
||||
res[i] = new CheckResult(Severity.Invalid, string.Format(V379, specieslist[e.Species], specieslist[splitSpecies]), CheckIdentifier.RelearnMove);
|
||||
}
|
||||
}
|
||||
|
||||
info.RelearnBase = baseMoves.ToArray();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
35
PKHeX.Core/Legality/Enums/CheckIdentifier.cs
Normal file
35
PKHeX.Core/Legality/Enums/CheckIdentifier.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
internal enum CheckIdentifier
|
||||
{
|
||||
Move,
|
||||
RelearnMove,
|
||||
Encounter,
|
||||
History,
|
||||
ECPID,
|
||||
Shiny,
|
||||
EC,
|
||||
PID,
|
||||
Gender,
|
||||
EVs,
|
||||
Language,
|
||||
Nickname,
|
||||
Trainer,
|
||||
IVs,
|
||||
None,
|
||||
Level,
|
||||
Ball,
|
||||
Memory,
|
||||
Geography,
|
||||
Form,
|
||||
Egg,
|
||||
Misc,
|
||||
Fateful,
|
||||
Ribbon,
|
||||
Training,
|
||||
Ability,
|
||||
Evolution,
|
||||
Special,
|
||||
Nature
|
||||
}
|
||||
}
|
11
PKHeX.Core/Legality/Enums/Severity.cs
Normal file
11
PKHeX.Core/Legality/Enums/Severity.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public enum Severity
|
||||
{
|
||||
Indeterminate = -2,
|
||||
Invalid = -1,
|
||||
Fishy = 0,
|
||||
Valid = 1,
|
||||
NotImplemented = 2,
|
||||
}
|
||||
}
|
19
PKHeX.Core/Legality/Structures/CheckResult.cs
Normal file
19
PKHeX.Core/Legality/Structures/CheckResult.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public class CheckResult
|
||||
{
|
||||
internal readonly Severity Judgement = Severity.Valid;
|
||||
internal string Comment = LegalityCheckStrings.V;
|
||||
public bool Valid => Judgement >= Severity.Fishy;
|
||||
public bool Flag;
|
||||
internal readonly CheckIdentifier Identifier;
|
||||
|
||||
internal CheckResult(CheckIdentifier i) { Identifier = i; }
|
||||
internal CheckResult(Severity s, string c, CheckIdentifier i)
|
||||
{
|
||||
Judgement = s;
|
||||
Comment = c;
|
||||
Identifier = i;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -340,7 +340,7 @@ namespace PKHeX.Core
|
|||
for (int i = 0; i < numslots; i++)
|
||||
{
|
||||
int level = data[ofs + i];
|
||||
int species = BitConverter.ToUInt16(data, ofs + numslots + i * 2);
|
||||
int species = BitConverter.ToUInt16(data, ofs + numslots + i * 2);
|
||||
slots[i] = new EncounterSlot
|
||||
{
|
||||
LevelMin = level,
|
||||
|
|
|
@ -12,68 +12,58 @@ namespace PKHeX.Core
|
|||
SpecialEncounter = 20,
|
||||
}
|
||||
|
||||
public class GBEncounterData
|
||||
public class GBEncounterData : IEncounterable
|
||||
{
|
||||
public readonly int Level;
|
||||
public int MoveLevel;
|
||||
public readonly int Species;
|
||||
public bool Gen2 => Generation == 2;
|
||||
public bool Gen1 => Generation == 1;
|
||||
public readonly int Generation;
|
||||
public readonly bool WasEgg;
|
||||
public readonly GBEncounterType Type;
|
||||
public readonly object Encounter;
|
||||
public readonly IEncounterable Encounter;
|
||||
|
||||
public int Species => Encounter.Species;
|
||||
public string Name => Encounter.Name;
|
||||
public bool EggEncounter => Encounter.EggEncounter;
|
||||
public int LevelMin => Encounter.LevelMin;
|
||||
public int LevelMax => Encounter.LevelMax;
|
||||
|
||||
// Egg encounter
|
||||
public GBEncounterData(int species)
|
||||
public GBEncounterData(int species, GameVersion game)
|
||||
{
|
||||
Generation = 2;
|
||||
Type = GBEncounterType.EggEncounter;
|
||||
Level = 5;
|
||||
WasEgg = true;
|
||||
Species = species;
|
||||
Encounter = new EncounterEgg { Species = species, Game = game, Level = Level };
|
||||
}
|
||||
|
||||
public GBEncounterData(PKM pkm, int gen, object enc)
|
||||
public GBEncounterData(PKM pkm, int gen, IEncounterable enc)
|
||||
{
|
||||
Generation = gen;
|
||||
Encounter = enc;
|
||||
WasEgg = false;
|
||||
if (Encounter is EncounterTrade)
|
||||
if (Encounter is EncounterTrade trade)
|
||||
{
|
||||
var trade = (EncounterTrade)Encounter;
|
||||
Species = trade.Species;
|
||||
if (pkm.HasOriginalMetLocation && trade.Level < pkm.Met_Level)
|
||||
Level = pkm.Met_Level; // Crystal
|
||||
else
|
||||
Level = trade.Level;
|
||||
if (Generation == 2)
|
||||
Type = GBEncounterType.TradeEncounterG2;
|
||||
else
|
||||
Type = GBEncounterType.TradeEncounterG1;
|
||||
Type = Generation == 2
|
||||
? GBEncounterType.TradeEncounterG2
|
||||
: GBEncounterType.TradeEncounterG1;
|
||||
}
|
||||
else if (Encounter is EncounterStatic)
|
||||
else if (Encounter is EncounterStatic statc)
|
||||
{
|
||||
var statc = (EncounterStatic)Encounter;
|
||||
Species = statc.Species;
|
||||
Level = statc.Level;
|
||||
if (statc.Moves != null && statc.Moves[0] != 0 && pkm.Moves.Contains(statc.Moves[0]))
|
||||
Type = GBEncounterType.SpecialEncounter;
|
||||
else
|
||||
Type = GBEncounterType.StaticEncounter;
|
||||
Type = statc.Moves != null && statc.Moves[0] != 0 && pkm.Moves.Contains(statc.Moves[0])
|
||||
? GBEncounterType.SpecialEncounter
|
||||
: GBEncounterType.StaticEncounter;
|
||||
}
|
||||
else if (Encounter is EncounterSlot1)
|
||||
else if (Encounter is EncounterSlot1 slot)
|
||||
{
|
||||
var slot = (EncounterSlot1)Encounter;
|
||||
Species = slot.Species;
|
||||
if (pkm.HasOriginalMetLocation && slot.LevelMin >= pkm.Met_Level && pkm.Met_Level <= slot.LevelMax)
|
||||
Level = pkm.Met_Level; // Crystal
|
||||
else
|
||||
Level = slot.LevelMin;
|
||||
Level = pkm.HasOriginalMetLocation && slot.LevelMin >= pkm.Met_Level && pkm.Met_Level <= slot.LevelMax
|
||||
? pkm.Met_Level // Crystal
|
||||
: slot.LevelMin;
|
||||
Type = GBEncounterType.WildEncounter;
|
||||
}
|
||||
MoveLevel = Level;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ namespace PKHeX.Core
|
|||
if (end < 0)
|
||||
return new int[0];
|
||||
count = Math.Min(count, 4);
|
||||
int start = Math.Max(end - count, 0);
|
||||
int start = end - count + 1;
|
||||
if (start < 0) start = 0;
|
||||
int[] result = new int[end - start + 1];
|
||||
Array.Copy(Moves, start, result, 0, result.Length);
|
||||
return result;
|
||||
|
|
|
@ -7,9 +7,13 @@ namespace PKHeX.Core
|
|||
{
|
||||
public int EncounterSpecies { get; set; }
|
||||
public DexLevel[][] EvolutionChains { get; set; }
|
||||
public List<int>[] validLevelUpMoves { get; set; }
|
||||
public List<int>[] validTMHMMoves { get; set; }
|
||||
public List<int>[] validTutorMoves { get; set; }
|
||||
public List<int>[] validLevelUpMoves { get; set; } = Empty;
|
||||
public List<int>[] validTMHMMoves { get; set; } = Empty;
|
||||
public List<int>[] validTutorMoves { get; set; } = Empty;
|
||||
public int[] Relearn = new int[0];
|
||||
public int minLvlG1 { get; set; }
|
||||
|
||||
private const int EmptyCount = 7;
|
||||
public static readonly List<int>[] Empty = new int[EmptyCount].Select(z => new List<int>()).ToArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -552,7 +552,7 @@ namespace PKHeX.Core
|
|||
#endregion
|
||||
#region Pokémon Link Gifts
|
||||
|
||||
private static readonly EncounterLink[] LinkGifts6 =
|
||||
internal static readonly EncounterLink[] LinkGifts6 =
|
||||
{
|
||||
new EncounterLink { Species = 154, Level = 50, Ability = 4, XY = true, ORAS = true }, // Meganium
|
||||
new EncounterLink { Species = 157, Level = 50, Ability = 4, XY = true, ORAS = true }, // Typhlosion
|
||||
|
|
|
@ -246,7 +246,7 @@ namespace PKHeX.Core
|
|||
};
|
||||
private static readonly EncounterArea[] Encounter_Pelago_SN = { new EncounterArea { Location = 30016, Slots = new[] { new EncounterSlot {Species = 627, LevelMin = 10, LevelMax = 55}, /* Rufflet SUN */ } } };
|
||||
private static readonly EncounterArea[] Encounter_Pelago_MN = { new EncounterArea { Location = 30016, Slots = new[] { new EncounterSlot {Species = 629, LevelMin = 10, LevelMax = 55}, /* Vullaby MOON */ } } };
|
||||
private static readonly EncounterTrade[] TradeGift_SM = // @ a\1\5\5
|
||||
internal static readonly EncounterTrade[] TradeGift_SM = // @ a\1\5\5
|
||||
{
|
||||
// Trades - 4.bin
|
||||
new EncounterTrade { Species = 066, Form = 0, Level = 09, Ability = 2, TID = 00410, SID = 00000, OTGender = 1, Gender = 0, Nature = Nature.Brave, }, // Machop
|
||||
|
|
|
@ -113,13 +113,13 @@ namespace PKHeX.Core
|
|||
{
|
||||
get
|
||||
{
|
||||
if (index < Table.Length)
|
||||
if (0 <= index && index < Table.Length)
|
||||
return Table[index];
|
||||
return Table[0];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (index < Table.Length)
|
||||
if (index < 0 || index >= Table.Length)
|
||||
return;
|
||||
Table[index] = value;
|
||||
}
|
||||
|
|
|
@ -252,11 +252,11 @@ namespace PKHeX.WinForms.Controls
|
|||
|
||||
// Refresh Move Legality
|
||||
for (int i = 0; i < 4; i++)
|
||||
movePB[i].Visible = !Legality.vMoves[i].Valid;
|
||||
movePB[i].Visible = !Legality.info.vMoves[i].Valid;
|
||||
|
||||
if (pkm.Format >= 6)
|
||||
for (int i = 0; i < 4; i++)
|
||||
relearnPB[i].Visible = !Legality.vRelearn[i].Valid;
|
||||
relearnPB[i].Visible = !Legality.info.vRelearn[i].Valid;
|
||||
|
||||
if (skipMoveRepop)
|
||||
return;
|
||||
|
|
|
@ -279,7 +279,7 @@ namespace PKHeX.WinForms
|
|||
fegform += CB_Form.SelectedIndex << 3;
|
||||
pkm[0x15] = (byte)fegform;
|
||||
|
||||
pkm[0x16] = (byte)Math.Min(Convert.ToInt32( TB_HPEV.Text), 252);
|
||||
pkm[0x16] = (byte)Math.Min(Convert.ToInt32(TB_HPEV.Text), 252);
|
||||
pkm[0x17] = (byte)Math.Min(Convert.ToInt32(TB_ATKEV.Text), 252);
|
||||
pkm[0x18] = (byte)Math.Min(Convert.ToInt32(TB_DEFEV.Text), 252);
|
||||
pkm[0x19] = (byte)Math.Min(Convert.ToInt32(TB_SPAEV.Text), 252);
|
||||
|
|
Loading…
Reference in a new issue