mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-27 22:40:22 +00:00
016a0e3aee
Certain calculations are best done with direct references. Indirectly fixed Shiny button bug (8 <= xor < 16) for past gen games.
1065 lines
55 KiB
C#
1065 lines
55 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
namespace PKHeX
|
||
{
|
||
public enum Severity
|
||
{
|
||
Indeterminate = -2,
|
||
Invalid = -1,
|
||
Fishy = 0,
|
||
Valid = 1,
|
||
NotImplemented = 2,
|
||
}
|
||
public class LegalityCheck
|
||
{
|
||
public Severity Judgement = Severity.Valid;
|
||
public string Comment = "Valid";
|
||
public bool Valid => Judgement >= Severity.Fishy;
|
||
public bool Flag;
|
||
|
||
public LegalityCheck() { }
|
||
public LegalityCheck(Severity s, string c)
|
||
{
|
||
Judgement = s;
|
||
Comment = c;
|
||
}
|
||
}
|
||
public partial class LegalityAnalysis
|
||
{
|
||
private LegalityCheck verifyGender()
|
||
{
|
||
if (PersonalTable.AO[pk6.Species].Gender == 255 && pk6.Gender != 2)
|
||
return new LegalityCheck(Severity.Invalid, "Genderless Pokémon should not have a gender.");
|
||
|
||
return new LegalityCheck();
|
||
}
|
||
private LegalityCheck verifyECPID()
|
||
{
|
||
// Secondary Checks
|
||
LegalityCheck c = new LegalityCheck();
|
||
if (pk6.EncryptionConstant == 0)
|
||
c = new LegalityCheck(Severity.Fishy, "Encryption Constant is not set.");
|
||
|
||
if (pk6.PID == 0)
|
||
c = new LegalityCheck(Severity.Fishy, "PID is not set.");
|
||
|
||
if (EncounterType == typeof (EncounterStatic))
|
||
{
|
||
var enc = (EncounterStatic) EncounterMatch;
|
||
if (enc.Shiny != null && (bool)enc.Shiny ^ pk6.IsShiny)
|
||
return new LegalityCheck(Severity.Invalid, "Encounter " + (enc.Shiny == true ? "must be" : "cannot be") + " shiny.");
|
||
}
|
||
|
||
string special = "";
|
||
if (pk6.Gen6)
|
||
{
|
||
// Wurmple -> Silcoon/Cascoon
|
||
int wIndex = Array.IndexOf(Legal.WurmpleFamily, pk6.Species);
|
||
if (wIndex > -1)
|
||
{
|
||
// Check if Wurmple was the origin (only Egg and Wild Encounter)
|
||
if (pk6.WasEgg || (EncounterType == typeof(EncounterSlot[]) && (EncounterMatch as EncounterSlot[]).All(slot => slot.Species == 265)))
|
||
if ((pk6.EncryptionConstant >> 16) % 10 / 5 != wIndex / 2)
|
||
return new LegalityCheck(Severity.Invalid, "Wurmple evolution Encryption Constant mismatch.");
|
||
}
|
||
else if (pk6.Species == 265)
|
||
special = "Wurmple Evolution: " + ((pk6.EncryptionConstant >> 16)%10/5 == 0 ? "Silcoon" : "Cascoon");
|
||
|
||
if (pk6.PID == pk6.EncryptionConstant)
|
||
return new LegalityCheck(Severity.Fishy, "Encryption Constant matches PID.");
|
||
|
||
int xor = pk6.TSV ^ pk6.PSV;
|
||
if (xor < 16 && xor >= 8 && (pk6.PID ^ 0x80000000) == pk6.EncryptionConstant)
|
||
return new LegalityCheck(Severity.Fishy, "Encryption Constant matches shinyxored PID.");
|
||
|
||
return special != "" ? new LegalityCheck(Severity.Valid, special) : c;
|
||
}
|
||
|
||
// When transferred to Generation 6, the Encryption Constant is copied from the PID.
|
||
// The PID is then checked to see if it becomes shiny with the new Shiny rules (>>4 instead of >>3)
|
||
// If the PID is nonshiny->shiny, the top bit is flipped.
|
||
|
||
// Check to see if the PID and EC are properly configured.
|
||
bool xorPID = ((pk6.TID ^ pk6.SID ^ (int)(pk6.PID & 0xFFFF) ^ (int)(pk6.PID >> 16)) & 0x7) == 8;
|
||
bool valid = xorPID
|
||
? pk6.EncryptionConstant == (pk6.PID ^ 0x8000000)
|
||
: pk6.EncryptionConstant == pk6.PID;
|
||
|
||
if (!valid)
|
||
if (xorPID)
|
||
return new LegalityCheck(Severity.Invalid, "PID should be equal to EC [with top bit flipped]!");
|
||
else
|
||
return new LegalityCheck(Severity.Invalid, "PID should be equal to EC!");
|
||
|
||
return c;
|
||
}
|
||
private LegalityCheck verifyNickname()
|
||
{
|
||
// If the Pokémon is not nicknamed, it should match one of the language strings.
|
||
if (pk6.Nickname.Length == 0)
|
||
return new LegalityCheck(Severity.Indeterminate, "Nickname is empty.");
|
||
if (pk6.Species > PKX.SpeciesLang[0].Length)
|
||
return new LegalityCheck(Severity.Indeterminate, "Species index invalid for Nickname comparison.");
|
||
if (!Encounter.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped Nickname check due to other check being invalid.");
|
||
|
||
if (pk6.Language > 8)
|
||
return new LegalityCheck(Severity.Indeterminate, "Language ID > 8.");
|
||
|
||
if (EncounterType == typeof(EncounterTrade))
|
||
{
|
||
string[] validOT = new string[0];
|
||
int index = -1;
|
||
if (pk6.XY)
|
||
{
|
||
validOT = Legal.TradeXY[pk6.Language];
|
||
index = Array.IndexOf(Legal.TradeGift_XY, EncounterMatch);
|
||
}
|
||
else if (pk6.AO)
|
||
{
|
||
validOT = Legal.TradeAO[pk6.Language];
|
||
index = Array.IndexOf(Legal.TradeGift_AO, EncounterMatch);
|
||
}
|
||
if (validOT.Length == 0)
|
||
return new LegalityCheck(Severity.Indeterminate, "Ingame Trade invalid version?");
|
||
if (index == -1 || validOT.Length < index * 2)
|
||
return new LegalityCheck(Severity.Indeterminate, "Ingame Trade invalid lookup?");
|
||
|
||
string nick = validOT[index];
|
||
string OT = validOT[validOT.Length/2 + index];
|
||
|
||
if (nick != pk6.Nickname)
|
||
return new LegalityCheck(Severity.Fishy, "Ingame Trade nickname has been altered.");
|
||
if (OT != pk6.OT_Name)
|
||
return new LegalityCheck(Severity.Invalid, "Ingame Trade OT has been altered.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Ingame Trade OT/Nickname have not been altered.");
|
||
}
|
||
|
||
if (pk6.IsEgg)
|
||
{
|
||
if (!pk6.IsNicknamed)
|
||
return new LegalityCheck(Severity.Invalid, "Eggs must be nicknamed.");
|
||
return PKX.SpeciesLang[pk6.Language][0] == pk6.Nickname
|
||
? new LegalityCheck(Severity.Valid, "Egg matches language Egg name.")
|
||
: new LegalityCheck(Severity.Invalid, "Egg name does not match language Egg name.");
|
||
}
|
||
string nickname = pk6.Nickname.Replace("'", "’");
|
||
if (pk6.IsNicknamed)
|
||
{
|
||
for (int i = 0; i < PKX.SpeciesLang.Length; i++)
|
||
{
|
||
string[] lang = PKX.SpeciesLang[i];
|
||
int index = Array.IndexOf(lang, nickname);
|
||
if (index < 0)
|
||
continue;
|
||
|
||
return index == pk6.Species && i != pk6.Language
|
||
? new LegalityCheck(Severity.Fishy, "Nickname matches another species name (+language).")
|
||
: new LegalityCheck(Severity.Fishy, "Nickname flagged, matches species name.");
|
||
}
|
||
return new LegalityCheck(Severity.Valid, "Nickname does not match another species name.");
|
||
}
|
||
// else
|
||
{
|
||
// Can't have another language name if it hasn't evolved or wasn't a language-traded egg.
|
||
return (pk6.WasTradedEgg || Legal.getHasEvolved(pk6)) && PKX.SpeciesLang.Any(lang => lang[pk6.Species] == nickname)
|
||
|| PKX.SpeciesLang[pk6.Language][pk6.Species] == nickname
|
||
? new LegalityCheck(Severity.Valid, "Nickname matches species name.")
|
||
: new LegalityCheck(Severity.Invalid, "Nickname does not match species name.");
|
||
}
|
||
}
|
||
private LegalityCheck verifyEVs()
|
||
{
|
||
var evs = pk6.EVs;
|
||
int sum = evs.Sum();
|
||
if (sum == 0 && pk6.Stat_Level - pk6.Met_Level > 0)
|
||
return new LegalityCheck(Severity.Fishy, "All EVs are zero, but leveled above Met Level");
|
||
if (sum == 508)
|
||
return new LegalityCheck(Severity.Fishy, "2 EVs remaining.");
|
||
if (sum > 510)
|
||
return new LegalityCheck(Severity.Invalid, "EV total cannot be above 510.");
|
||
if (evs.Any(ev => ev > 252))
|
||
return new LegalityCheck(Severity.Invalid, "EVs cannot go above 252.");
|
||
if (evs.All(ev => pk6.EVs[0] == ev) && evs[0] != 0)
|
||
return new LegalityCheck(Severity.Fishy, "EVs are all equal.");
|
||
|
||
return new LegalityCheck();
|
||
}
|
||
private LegalityCheck verifyIVs()
|
||
{
|
||
if (EncounterType == typeof(EncounterStatic) && (EncounterMatch as EncounterStatic)?.IV3 == true)
|
||
if (pk6.IVs.Count(iv => iv == 31) < 3)
|
||
return new LegalityCheck(Severity.Invalid, "Should have at least 3 IVs = 31.");
|
||
if (pk6.IVs.Sum() == 0)
|
||
return new LegalityCheck(Severity.Fishy, "All IVs are zero.");
|
||
if (pk6.IVs[0] < 30 && pk6.IVs.All(iv => pk6.IVs[0] == iv))
|
||
return new LegalityCheck(Severity.Fishy, "All IVs are equal.");
|
||
return new LegalityCheck();
|
||
}
|
||
private LegalityCheck verifyID()
|
||
{
|
||
if (EncounterType == typeof(EncounterTrade))
|
||
return new LegalityCheck(); // Already matches Encounter Trade information
|
||
if (pk6.TID == 0 && pk6.SID == 0)
|
||
return new LegalityCheck(Severity.Fishy, "TID and SID are zero.");
|
||
if (pk6.TID == pk6.SID)
|
||
return new LegalityCheck(Severity.Fishy, "TID and SID are equal.");
|
||
if (pk6.TID == 0)
|
||
return new LegalityCheck(Severity.Fishy, "TID is zero.");
|
||
if (pk6.SID == 0)
|
||
return new LegalityCheck(Severity.Fishy, "SID is zero.");
|
||
return new LegalityCheck();
|
||
}
|
||
private LegalityCheck verifyEncounter()
|
||
{
|
||
if (!pk6.Gen6)
|
||
return new LegalityCheck {Judgement = Severity.NotImplemented};
|
||
|
||
if (pk6.WasLink)
|
||
{
|
||
// Should NOT be Fateful, and should be in Database
|
||
EncounterLink enc = EncounterMatch as EncounterLink;
|
||
if (enc == null)
|
||
return new LegalityCheck(Severity.Invalid, "Invalid Link Gift: unable to find matching gift.");
|
||
|
||
if (pk6.XY && !enc.XY)
|
||
return new LegalityCheck(Severity.Invalid, "Invalid Link Gift: can't obtain in XY.");
|
||
if (pk6.AO && !enc.ORAS)
|
||
return new LegalityCheck(Severity.Invalid, "Invalid Link Gift: can't obtain in ORAS.");
|
||
|
||
if (enc.Shiny != null && (bool)enc.Shiny ^ pk6.IsShiny)
|
||
return new LegalityCheck(Severity.Invalid, "Shiny Link gift mismatch.");
|
||
|
||
return pk6.FatefulEncounter
|
||
? new LegalityCheck(Severity.Invalid, "Invalid Link Gift: should not be Fateful Encounter.")
|
||
: new LegalityCheck(Severity.Valid, "Valid Link gift.");
|
||
}
|
||
if (pk6.WasEvent || pk6.WasEventEgg)
|
||
{
|
||
WC6 MatchedWC6 = EncounterMatch as WC6;
|
||
return MatchedWC6 != null // Matched in RelearnMoves check.
|
||
? new LegalityCheck(Severity.Valid, $"Matches #{MatchedWC6.CardID.ToString("0000")} ({MatchedWC6.CardTitle})")
|
||
: new LegalityCheck(Severity.Invalid, "Not a valid Wonder Card gift.");
|
||
}
|
||
|
||
EncounterMatch = null; // Reset object
|
||
if (pk6.WasEgg)
|
||
{
|
||
// Check Hatch Locations
|
||
if (pk6.Met_Level != 1)
|
||
return new LegalityCheck(Severity.Invalid, "Invalid met level, expected 1.");
|
||
// Check species
|
||
if (Legal.NoHatchFromEgg.Contains(pk6.Species))
|
||
return new LegalityCheck(Severity.Invalid, "Species cannot be hatched from an egg.");
|
||
if (pk6.IsEgg)
|
||
{
|
||
if (pk6.Egg_Location == 30002)
|
||
return new LegalityCheck(Severity.Invalid, "Egg location shouldn't be 'traded' for an un-hatched egg.");
|
||
|
||
if (pk6.Met_Location == 30002)
|
||
return new LegalityCheck(Severity.Valid, "Valid traded un-hatched egg.");
|
||
return pk6.Met_Location == 0
|
||
? new LegalityCheck(Severity.Valid, "Valid un-hatched egg.")
|
||
: new LegalityCheck(Severity.Invalid, "Invalid location for un-hatched egg (expected no met location).");
|
||
}
|
||
if (pk6.XY)
|
||
{
|
||
if (pk6.Egg_Location == 318)
|
||
return new LegalityCheck(Severity.Invalid, "Invalid X/Y egg location.");
|
||
return Legal.ValidMet_XY.Contains(pk6.Met_Location)
|
||
? new LegalityCheck(Severity.Valid, "Valid X/Y hatched egg.")
|
||
: new LegalityCheck(Severity.Invalid, "Invalid X/Y location for hatched egg.");
|
||
}
|
||
if (pk6.AO)
|
||
{
|
||
return Legal.ValidMet_AO.Contains(pk6.Met_Location)
|
||
? new LegalityCheck(Severity.Valid, "Valid OR/AS hatched egg.")
|
||
: new LegalityCheck(Severity.Invalid, "Invalid OR/AS location for hatched egg.");
|
||
}
|
||
return new LegalityCheck(Severity.Invalid, "Invalid location for hatched egg.");
|
||
}
|
||
|
||
EncounterMatch = Legal.getValidStaticEncounter(pk6);
|
||
if (EncounterMatch != null)
|
||
return new LegalityCheck(Severity.Valid, "Valid gift/static encounter.");
|
||
|
||
if (Legal.getIsFossil(pk6))
|
||
{
|
||
return pk6.AbilityNumber != 4
|
||
? new LegalityCheck(Severity.Valid, "Valid revived fossil.")
|
||
: new LegalityCheck(Severity.Invalid, "Hidden ability on revived fossil.");
|
||
}
|
||
EncounterMatch = Legal.getValidFriendSafari(pk6);
|
||
if (EncounterMatch != null)
|
||
{
|
||
if (pk6.Species == 670 || pk6.Species == 671) // Floette
|
||
if (!new[] {0, 1, 3}.Contains(pk6.AltForm)) // 0/1/3 - RBY
|
||
return new LegalityCheck(Severity.Invalid, "Friend Safari: Not valid color.");
|
||
else if (pk6.Species == 710 || pk6.Species == 711) // Pumpkaboo
|
||
if (pk6.AltForm != 1) // Average
|
||
return new LegalityCheck(Severity.Invalid, "Friend Safari: Not average sized.");
|
||
else if (pk6.Species == 586) // Sawsbuck
|
||
if (pk6.AltForm != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Friend Safari: Not Spring form.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Valid friend safari encounter.");
|
||
}
|
||
|
||
EncounterMatch = Legal.getValidWildEncounters(pk6);
|
||
if (EncounterMatch != null)
|
||
{
|
||
EncounterSlot[] enc = (EncounterSlot[])EncounterMatch;
|
||
|
||
if (enc.Any(slot => slot.Normal))
|
||
return enc.All(slot => slot.Pressure)
|
||
? new LegalityCheck(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).")
|
||
: new LegalityCheck(Severity.Valid, "Valid encounter at location.");
|
||
|
||
// Decreased Level Encounters
|
||
if (enc.Any(slot => slot.WhiteFlute))
|
||
return enc.All(slot => slot.Pressure)
|
||
? new LegalityCheck(Severity.Valid, "Valid encounter at location (White Flute & Pressure/Hustle/Vital Spirit).")
|
||
: new LegalityCheck(Severity.Valid, "Valid encounter at location (White Flute).");
|
||
|
||
// Increased Level Encounters
|
||
if (enc.Any(slot => slot.BlackFlute))
|
||
return enc.All(slot => slot.Pressure)
|
||
? new LegalityCheck(Severity.Valid, "Valid encounter at location (Black Flute & Pressure/Hustle/Vital Spirit).")
|
||
: new LegalityCheck(Severity.Valid, "Valid encounter at location (Black Flute).");
|
||
|
||
if (enc.Any(slot => slot.Pressure))
|
||
return new LegalityCheck(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Valid encounter at location (DexNav).");
|
||
}
|
||
EncounterMatch = Legal.getValidIngameTrade(pk6);
|
||
if (EncounterMatch != null)
|
||
{
|
||
return new LegalityCheck(Severity.Valid, "Valid ingame trade.");
|
||
}
|
||
return new LegalityCheck(Severity.Invalid, "Not a valid encounter.");
|
||
}
|
||
private LegalityCheck verifyLevel()
|
||
{
|
||
WC6 MatchedWC6 = EncounterMatch as WC6;
|
||
if (MatchedWC6 != null && MatchedWC6.Level != pk6.Met_Level)
|
||
return new LegalityCheck(Severity.Invalid, "Met Level does not match Wonder Card level.");
|
||
|
||
int lvl = pk6.CurrentLevel;
|
||
if (lvl < pk6.Met_Level)
|
||
return new LegalityCheck(Severity.Invalid, "Current level is below met level.");
|
||
if ((pk6.WasEgg || EncounterMatch == null) && !Legal.getEvolutionValid(pk6) && pk6.Species != 350)
|
||
return new LegalityCheck(Severity.Invalid, "Level is below evolution requirements.");
|
||
if (lvl > pk6.Met_Level && lvl > 1 && lvl != 100 && pk6.EXP == PKX.getEXP(pk6.Stat_Level, pk6.Species))
|
||
return new LegalityCheck(Severity.Fishy, "Current experience matches level threshold.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Current level is not below met level.");
|
||
}
|
||
private LegalityCheck verifyRibbons()
|
||
{
|
||
if (!Encounter.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped Ribbon check due to other check being invalid.");
|
||
|
||
List<string> missingRibbons = new List<string>();
|
||
List<string> invalidRibbons = new List<string>();
|
||
|
||
// Check Event Ribbons
|
||
bool[] EventRib =
|
||
{
|
||
pk6.RibbonCountry, pk6.RibbonNational, pk6.RibbonEarth, pk6.RibbonWorld, pk6.RibbonClassic,
|
||
pk6.RibbonPremier, pk6.RibbonEvent, pk6.RibbonBirthday, pk6.RibbonSpecial, pk6.RibbonSouvenir,
|
||
pk6.RibbonWishing, pk6.RibbonChampionBattle, pk6.RibbonChampionRegional, pk6.RibbonChampionNational, pk6.RibbonChampionWorld
|
||
};
|
||
WC6 MatchedWC6 = EncounterMatch as WC6;
|
||
if (MatchedWC6 != null) // Wonder Card
|
||
{
|
||
bool[] wc6rib =
|
||
{
|
||
MatchedWC6.RibbonCountry, MatchedWC6.RibbonNational, MatchedWC6.RibbonEarth, MatchedWC6.RibbonWorld, MatchedWC6.RibbonClassic,
|
||
MatchedWC6.RibbonPremier, MatchedWC6.RibbonEvent, MatchedWC6.RibbonBirthday, MatchedWC6.RibbonSpecial, MatchedWC6.RibbonSouvenir,
|
||
MatchedWC6.RibbonWishing, MatchedWC6.RibbonChampionBattle, MatchedWC6.RibbonChampionRegional, MatchedWC6.RibbonChampionNational, MatchedWC6.RibbonChampionWorld
|
||
};
|
||
for (int i = 0; i < EventRib.Length; i++)
|
||
if (EventRib[i] ^ wc6rib[i]) // Mismatch
|
||
(wc6rib[i] ? missingRibbons : invalidRibbons).Add(EventRibName[i]);
|
||
}
|
||
else if (EncounterType == typeof(EncounterLink))
|
||
{
|
||
// No Event Ribbons except Classic (unless otherwise specified, ie not for Demo)
|
||
for (int i = 0; i < EventRib.Length; i++)
|
||
if (i != 4 && EventRib[i])
|
||
invalidRibbons.Add(EventRibName[i]);
|
||
|
||
if (EventRib[4] ^ ((EncounterLink)EncounterMatch).Classic)
|
||
(EventRib[4] ? invalidRibbons : missingRibbons).Add(EventRibName[4]);
|
||
}
|
||
else // No ribbons
|
||
{
|
||
for (int i = 0; i < EventRib.Length; i++)
|
||
if (EventRib[i])
|
||
invalidRibbons.Add(EventRibName[i]);
|
||
}
|
||
|
||
// Unobtainable ribbons for Gen6 Origin
|
||
if (pk6.RibbonChampionG3Hoenn)
|
||
invalidRibbons.Add("GBA Champion"); // RSE HoF
|
||
if (pk6.RibbonChampionSinnoh)
|
||
invalidRibbons.Add("Sinnoh Champ"); // DPPt HoF
|
||
if (pk6.RibbonArtist)
|
||
invalidRibbons.Add("Artist"); // RSE Master Rank Portrait
|
||
if (pk6.RibbonRecord)
|
||
invalidRibbons.Add("Record"); // Unobtainable
|
||
if (pk6.RibbonLegend)
|
||
invalidRibbons.Add("Legend"); // HGSS Defeat Red @ Mt.Silver
|
||
if (pk6.RibbonCountMemoryContest > 0)
|
||
invalidRibbons.Add("Contest Memory"); // Gen3/4 Contest
|
||
if (pk6.RibbonCountMemoryBattle > 0)
|
||
invalidRibbons.Add("Battle Memory"); // Gen3/4 Battle
|
||
|
||
if (missingRibbons.Count + invalidRibbons.Count == 0)
|
||
return new LegalityCheck(Severity.Valid, "All ribbons accounted for.");
|
||
|
||
string[] result = new string[2];
|
||
if (missingRibbons.Count > 0)
|
||
result[0] = "Missing Ribbons: " + string.Join(", ", missingRibbons);
|
||
if (invalidRibbons.Count > 0)
|
||
result[1] = "Invalid Ribbons: " + string.Join(", ", invalidRibbons);
|
||
return new LegalityCheck(Severity.Invalid, string.Join(Environment.NewLine, result.Where(s=>!string.IsNullOrEmpty(s))));
|
||
}
|
||
private LegalityCheck verifyAbility()
|
||
{
|
||
int[] abilities = PersonalTable.AO.getAbilities(pk6.Species, pk6.AltForm);
|
||
int abilval = Array.IndexOf(abilities, pk6.Ability);
|
||
if (abilval < 0)
|
||
return new LegalityCheck(Severity.Invalid, "Ability is not valid for species/form.");
|
||
|
||
if (EncounterMatch != null)
|
||
{
|
||
if (EncounterType == typeof(EncounterStatic))
|
||
if (pk6.AbilityNumber == 4 ^ ((EncounterStatic)EncounterMatch).Ability == 4)
|
||
return new LegalityCheck(Severity.Invalid, "Hidden Ability mismatch for static encounter.");
|
||
if (EncounterType == typeof(EncounterTrade))
|
||
if (pk6.AbilityNumber == 4 ^ ((EncounterTrade)EncounterMatch).Ability == 4)
|
||
return new LegalityCheck(Severity.Invalid, "Hidden Ability mismatch for ingame trade.");
|
||
if (EncounterType == typeof(EncounterSlot[]) && pk6.AbilityNumber == 4)
|
||
{
|
||
var slots = (EncounterSlot[])EncounterMatch;
|
||
bool valid = slots.Any(slot => slot.DexNav ||
|
||
slot.Type == SlotType.FriendSafari ||
|
||
slot.Type == SlotType.Horde);
|
||
|
||
if (!valid)
|
||
return new LegalityCheck(Severity.Invalid, "Hidden Ability on non-horde/friend safari wild encounter.");
|
||
}
|
||
}
|
||
|
||
return abilities[pk6.AbilityNumber >> 1] != pk6.Ability
|
||
? new LegalityCheck(Severity.Invalid, "Ability does not match ability number.")
|
||
: new LegalityCheck(Severity.Valid, "Ability matches ability number.");
|
||
}
|
||
private LegalityCheck verifyBall()
|
||
{
|
||
if (!pk6.Gen6)
|
||
return new LegalityCheck();
|
||
if (!Encounter.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped Ball check due to other check being invalid.");
|
||
|
||
if (EncounterType == typeof(WC6))
|
||
return pk6.Ball != ((WC6)EncounterMatch).Pokéball
|
||
? new LegalityCheck(Severity.Invalid, "Ball does not match specified Wonder Card Ball.")
|
||
: new LegalityCheck(Severity.Valid, "Ball matches Wonder Card.");
|
||
if (EncounterType == typeof(EncounterLink))
|
||
return ((EncounterLink)EncounterMatch).Ball != pk6.Ball
|
||
? new LegalityCheck(Severity.Invalid, "Incorrect ball on Link gift.")
|
||
: new LegalityCheck(Severity.Valid, "Correct ball on Link gift.");
|
||
if (EncounterType == typeof(EncounterTrade))
|
||
return pk6.Ball != 4 // Pokeball
|
||
? new LegalityCheck(Severity.Invalid, "Incorrect ball on ingame trade encounter.")
|
||
: new LegalityCheck(Severity.Valid, "Correct ball on ingame trade encounter.");
|
||
|
||
if (pk6.Ball == 0x04) // Poké Ball
|
||
return new LegalityCheck(Severity.Valid, "Standard Poké Ball.");
|
||
|
||
if (EncounterType == typeof(EncounterStatic))
|
||
{
|
||
EncounterStatic enc = EncounterMatch as EncounterStatic;
|
||
if (enc.Gift)
|
||
return enc.Ball != pk6.Ball // Pokéball by default
|
||
? new LegalityCheck(Severity.Invalid, "Incorrect ball on ingame gift.")
|
||
: new LegalityCheck(Severity.Valid, "Correct ball on ingame gift.");
|
||
|
||
return !Legal.WildPokeballs.Contains(pk6.Ball)
|
||
? new LegalityCheck(Severity.Invalid, "Incorrect ball on ingame static encounter.")
|
||
: new LegalityCheck(Severity.Valid, "Correct ball on ingame static encounter.");
|
||
}
|
||
if (EncounterType == typeof(EncounterSlot[]))
|
||
return !Legal.WildPokeballs.Contains(pk6.Ball)
|
||
? new LegalityCheck(Severity.Invalid, "Incorrect ball on ingame encounter.")
|
||
: new LegalityCheck(Severity.Valid, "Correct ball on ingame encounter.");
|
||
|
||
if (pk6.WasEgg)
|
||
{
|
||
if (pk6.Ball == 0x01) // Master Ball
|
||
return new LegalityCheck(Severity.Invalid, "Master Ball on egg origin.");
|
||
if (pk6.Ball == 0x10) // Cherish Ball
|
||
return new LegalityCheck(Severity.Invalid, "Cherish Ball on non-event.");
|
||
|
||
if (pk6.Gender == 2) // Genderless
|
||
return pk6.Ball != 0x04 // Must be Pokéball as ball can only pass via mother (not Ditto!)
|
||
? new LegalityCheck(Severity.Invalid, "Non-Pokéball on genderless egg.")
|
||
: new LegalityCheck(Severity.Valid, "Pokéball on genderless egg.");
|
||
if (Legal.BreedMaleOnly.Contains(pk6.Species))
|
||
return pk6.Ball != 0x04 // Must be Pokéball as ball can only pass via mother (not Ditto!)
|
||
? new LegalityCheck(Severity.Invalid, "Non-Pokéball on Male-Only egg.")
|
||
: new LegalityCheck(Severity.Valid, "Pokéball on Male-Only egg.");
|
||
|
||
if (pk6.Ball == 0x05) // Safari Ball
|
||
{
|
||
if (Legal.getLineage(pk6).All(e => !Legal.Inherit_Safari.Contains(e)))
|
||
return new LegalityCheck(Severity.Invalid, "Safari Ball not possible for species.");
|
||
if (pk6.AbilityNumber == 4)
|
||
return new LegalityCheck(Severity.Invalid, "Safari Ball with Hidden Ability.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Safari Ball possible for species.");
|
||
}
|
||
if (0x10 < pk6.Ball && pk6.Ball < 0x18) // Apricorn Ball
|
||
{
|
||
if (Legal.getLineage(pk6).All(e => !Legal.Inherit_Apricorn.Contains(e)))
|
||
return new LegalityCheck(Severity.Invalid, "Apricorn Ball not possible for species.");
|
||
if (pk6.AbilityNumber == 4)
|
||
return new LegalityCheck(Severity.Invalid, "Apricorn Ball with Hidden Ability.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Apricorn Ball possible for species.");
|
||
}
|
||
if (pk6.Ball == 0x18) // Sport Ball
|
||
{
|
||
if (Legal.getLineage(pk6).All(e => !Legal.Inherit_Sport.Contains(e)))
|
||
return new LegalityCheck(Severity.Invalid, "Sport Ball not possible for species.");
|
||
if (pk6.AbilityNumber == 4)
|
||
return new LegalityCheck(Severity.Invalid, "Sport Ball with Hidden Ability.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Sport Ball possible for species.");
|
||
}
|
||
if (pk6.Ball == 0x19) // Dream Ball
|
||
{
|
||
if (Legal.getLineage(pk6).All(e => !Legal.Inherit_Dream.Contains(e)))
|
||
return new LegalityCheck(Severity.Invalid, "Dream Ball not possible for species.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Dream Ball possible for species.");
|
||
}
|
||
|
||
if (pk6.Species > 650 && pk6.Species != 700) // Sylveon
|
||
return !Legal.WildPokeballs.Contains(pk6.Ball)
|
||
? new LegalityCheck(Severity.Invalid, "Unobtainable ball for Kalos origin.")
|
||
: new LegalityCheck(Severity.Valid, "Obtainable ball for Kalos origin.");
|
||
|
||
if (0x0D <= pk6.Ball && pk6.Ball <= 0x0F)
|
||
{
|
||
if (Legal.Ban_Gen4Ball.Contains(pk6.Species))
|
||
return new LegalityCheck(Severity.Invalid, "Unobtainable capture for Gen4 Ball.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Obtainable capture for Gen4 Ball.");
|
||
}
|
||
if (0x02 <= pk6.Ball && pk6.Ball <= 0x0C) // Don't worry, Ball # 0x05 was already checked.
|
||
{
|
||
if (Legal.Ban_Gen3Ball.Contains(pk6.Species))
|
||
return new LegalityCheck(Severity.Invalid, "Unobtainable capture for Gen4 Ball.");
|
||
if (pk6.AbilityNumber == 4 && 152 <= pk6.Species && pk6.Species <= 160)
|
||
return new LegalityCheck(Severity.Invalid, "Ball not possible for species with hidden ability.");
|
||
|
||
return new LegalityCheck(Severity.Valid, "Obtainable capture for Gen4 Ball.");
|
||
}
|
||
}
|
||
|
||
return new LegalityCheck(Severity.Invalid, "No ball check satisfied, assuming illegal.");
|
||
}
|
||
private LegalityCheck verifyHistory()
|
||
{
|
||
if (!Encounter.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped History check due to other check being invalid.");
|
||
|
||
WC6 MatchedWC6 = EncounterMatch as WC6;
|
||
if (MatchedWC6?.OT.Length > 0) // Has Event OT -- null propagation yields false if MatchedWC6=null
|
||
{
|
||
if (pk6.OT_Friendship != PersonalTable.AO[pk6.Species].BaseFriendship)
|
||
return new LegalityCheck(Severity.Invalid, "Event OT Friendship does not match base friendship.");
|
||
if (pk6.OT_Affection != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Event OT Affection should be zero.");
|
||
if (pk6.CurrentHandler != 1)
|
||
return new LegalityCheck(Severity.Invalid, "Current handler should not be Event OT.");
|
||
}
|
||
if (!pk6.WasEvent && !(pk6.WasLink && (EncounterMatch as EncounterLink)?.OT == false) && (pk6.HT_Name.Length == 0 || pk6.Geo1_Country == 0)) // Is not Traded
|
||
{
|
||
if (pk6.HT_Name.Length != 0)
|
||
return new LegalityCheck(Severity.Invalid, "GeoLocation -- HT Name present but has no previous Country.");
|
||
if (pk6.Geo1_Country != 0)
|
||
return new LegalityCheck(Severity.Invalid, "GeoLocation -- Previous country of residence but no Handling Trainer.");
|
||
if (pk6.HT_Memory != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Memory -- Handling Trainer memory present but no Handling Trainer.");
|
||
if (pk6.CurrentHandler != 0) // Badly edited; PKHeX doesn't trip this.
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- Current handler should not be the Handling Trainer.");
|
||
if (pk6.HT_Friendship != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- Handling Trainer Friendship should be zero.");
|
||
if (pk6.HT_Affection != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- Handling Trainer Affection should be zero.");
|
||
if (pk6.XY && pk6.CNTs.Any(stat => stat > 0))
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- Contest stats on XY should be zero.");
|
||
|
||
// We know it is untraded (HT is empty), if it must be trade evolved flag it.
|
||
if (Legal.getHasTradeEvolved(pk6) && (EncounterMatch as EncounterSlot[])?.Any(slot => slot.Species == pk6.Species) != true)
|
||
{
|
||
if (pk6.Species != 350) // Milotic
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- requires a trade evolution.");
|
||
if (pk6.CNT_Beauty < 170) // Beauty Contest Stat Requirement
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- Beauty is not high enough for Levelup Evolution.");
|
||
if (pk6.CurrentLevel == 1)
|
||
return new LegalityCheck(Severity.Invalid, "Untraded -- Beauty is high enough but still Level 1.");
|
||
}
|
||
}
|
||
else // Is Traded
|
||
{
|
||
if (pk6.HT_Memory == 0)
|
||
return new LegalityCheck(Severity.Invalid, "Memory -- missing Handling Trainer Memory.");
|
||
}
|
||
|
||
// Memory Checks
|
||
if (pk6.IsEgg)
|
||
{
|
||
if (pk6.HT_Memory != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Memory -- has Handling Trainer Memory.");
|
||
if (pk6.OT_Memory != 0)
|
||
return new LegalityCheck(Severity.Invalid, "Memory -- has Original Trainer Memory.");
|
||
}
|
||
else if (EncounterType != typeof(WC6))
|
||
{
|
||
if (pk6.OT_Memory == 0 ^ !pk6.Gen6)
|
||
return new LegalityCheck(Severity.Invalid, "Memory -- missing Original Trainer Memory.");
|
||
if (!pk6.Gen6 && pk6.OT_Affection != 0)
|
||
return new LegalityCheck(Severity.Invalid, "OT Affection should be zero.");
|
||
}
|
||
// Unimplemented: Ingame Trade Memories
|
||
|
||
return new LegalityCheck(Severity.Valid, "History is valid.");
|
||
}
|
||
private LegalityCheck verifyCommonMemory(int handler)
|
||
{
|
||
int m = 0;
|
||
int t = 0;
|
||
string resultPrefix = "";
|
||
switch (handler)
|
||
{
|
||
case 0:
|
||
m = pk6.OT_Memory;
|
||
t = pk6.OT_TextVar;
|
||
resultPrefix = "OT ";
|
||
break;
|
||
case 1:
|
||
m = pk6.HT_Memory;
|
||
t = pk6.HT_TextVar;
|
||
resultPrefix = "HT ";
|
||
break;
|
||
}
|
||
int matchingMoveMemory = Array.IndexOf(Legal.MoveSpecificMemories[0], m);
|
||
if (matchingMoveMemory != -1 && pk6.Species != 235 && !Legal.getCanLearnMachineMove(pk6, Legal.MoveSpecificMemories[1][matchingMoveMemory]))
|
||
{
|
||
return new LegalityCheck(Severity.Invalid, resultPrefix + "Memory: Species cannot learn this move.");
|
||
}
|
||
if (m == 6 && !Legal.LocationsWithPKCenter[0].Contains(t))
|
||
{
|
||
return new LegalityCheck(Severity.Invalid, resultPrefix + "Memory: Location doesn't have a Pokemon Center.");
|
||
}
|
||
if (m == 21) // {0} saw {2} carrying {1} on its back. {4} that {3}.
|
||
{
|
||
if (!Legal.getCanLearnMachineMove(new PK6 {Species = t, EXP = PKX.getEXP(100, t)}, 19))
|
||
return new LegalityCheck(Severity.Invalid, resultPrefix + "Memory: Argument Species cannot learn Fly.");
|
||
}
|
||
if ((m == 16 || m == 48) && (t == 0 || !Legal.getCanKnowMove(pk6, t, 1)))
|
||
{
|
||
return new LegalityCheck(Severity.Invalid, resultPrefix + "Memory: Species cannot know this move.");
|
||
}
|
||
if (m == 49 && (t == 0 || !Legal.getCanRelearnMove(pk6, t, 1))) // {0} was able to remember {2} at {1}'s instruction. {4} that {3}.
|
||
{
|
||
return new LegalityCheck(Severity.Invalid, resultPrefix + "Memory: Species cannot relearn this move.");
|
||
}
|
||
return new LegalityCheck(Severity.Valid, resultPrefix + "Memory is valid.");
|
||
}
|
||
private LegalityCheck verifyOTMemory()
|
||
{
|
||
if (!History.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped OT Memory check as History is not valid.");
|
||
|
||
if (EncounterType == typeof(EncounterTrade))
|
||
{
|
||
return new LegalityCheck(Severity.Valid, "OT Memory (Ingame Trade) is valid.");
|
||
}
|
||
if (EncounterType == typeof(WC6))
|
||
{
|
||
WC6 MatchedWC6 = EncounterMatch as WC6;
|
||
if (pk6.OT_Memory != MatchedWC6.OT_Memory)
|
||
return new LegalityCheck(Severity.Invalid, "Event " + (MatchedWC6.OT_Memory == 0 ? "should not have an OT Memory" : "OT Memory should be index " + MatchedWC6.OT_Memory) + ".");
|
||
if (pk6.OT_Intensity != MatchedWC6.OT_Intensity)
|
||
return new LegalityCheck(Severity.Invalid, "Event " + (MatchedWC6.OT_Intensity == 0 ? "should not have an OT Memory Intensity value" : "OT Memory Intensity should be index " + MatchedWC6.OT_Intensity) + ".");
|
||
if (pk6.OT_TextVar != MatchedWC6.OT_TextVar)
|
||
return new LegalityCheck(Severity.Invalid, "Event " + (MatchedWC6.OT_TextVar == 0 ? "should not have an OT Memory TextVar value" : "OT Memory TextVar should be index " + MatchedWC6.OT_TextVar) + ".");
|
||
if (pk6.OT_Feeling != MatchedWC6.OT_Feeling)
|
||
return new LegalityCheck(Severity.Invalid, "Event " + (MatchedWC6.OT_Feeling == 0 ? "should not have an OT Memory Feeling value" : "OT Memory Feeling should be index " + MatchedWC6.OT_Feeling) + ".");
|
||
}
|
||
switch (pk6.OT_Memory)
|
||
{
|
||
case 2: // {0} hatched from an Egg and saw {1} for the first time at... {2}. {4} that {3}.
|
||
if (!pk6.WasEgg)
|
||
return new LegalityCheck(Severity.Invalid, "OT Memory: OT did not hatch this.");
|
||
return new LegalityCheck(Severity.Valid, "OT Memory is valid.");
|
||
case 4: // {0} became {1}’s friend when it arrived via Link Trade at... {2}. {4} that {3}.
|
||
return new LegalityCheck(Severity.Invalid, "OT Memory: Link Trade is not a valid first memory.");
|
||
case 6: // {0} went to the Pokémon Center in {2} with {1} and had its tired body healed there. {4} that {3}.
|
||
int matchingOriginGame = Array.IndexOf(Legal.LocationsWithPKCenter[0], pk6.OT_TextVar);
|
||
if (matchingOriginGame != -1)
|
||
{
|
||
int gameID = Legal.LocationsWithPKCenter[1][matchingOriginGame];
|
||
if (pk6.XY && gameID != 0 || pk6.AO && gameID != 1)
|
||
return new LegalityCheck(Severity.Invalid, "OT Memory: Location doesn't exist on Origin Game region.");
|
||
}
|
||
return verifyCommonMemory(0);
|
||
case 14:
|
||
if (!Legal.getCanBeCaptured(pk6.OT_TextVar, pk6.Version))
|
||
return new LegalityCheck(Severity.Invalid, "OT Memory: Captured Species can not be captured in game.");
|
||
return new LegalityCheck(Severity.Valid, "OT Memory: Captured Species can be captured in game.");
|
||
}
|
||
if (pk6.XY && Legal.Memory_NotXY.Contains(pk6.OT_Memory))
|
||
return new LegalityCheck(Severity.Invalid, "OT Memory: OR/AS exclusive memory on X/Y origin.");
|
||
if (pk6.AO && Legal.Memory_NotAO.Contains(pk6.OT_Memory))
|
||
return new LegalityCheck(Severity.Invalid, "OT Memory: X/Y exclusive memory on OR/AS origin.");
|
||
|
||
return verifyCommonMemory(0);
|
||
}
|
||
private LegalityCheck verifyHTMemory()
|
||
{
|
||
if (!History.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped HT Memory check as History is not valid.");
|
||
|
||
switch (pk6.HT_Memory)
|
||
{
|
||
case 1: // {0} met {1} at... {2}. {1} threw a Poké Ball at it, and they started to travel together. {4} that {3}.
|
||
return new LegalityCheck(Severity.Invalid, "HT Memory: Handling Trainer did not capture this.");
|
||
case 2: // {0} hatched from an Egg and saw {1} for the first time at... {2}. {4} that {3}.
|
||
return new LegalityCheck(Severity.Invalid, "HT Memory: Handling Trainer did not hatch this.");
|
||
case 14:
|
||
if (!Legal.getCanBeCaptured(pk6.HT_TextVar))
|
||
return new LegalityCheck(Severity.Invalid, "HT Memory: Captured Species can not be captured in game.");
|
||
return new LegalityCheck(Severity.Valid, "HT Memory: Captured Species can be captured in game.");
|
||
}
|
||
return verifyCommonMemory(1);
|
||
}
|
||
private LegalityCheck verifyRegion()
|
||
{
|
||
bool valid = false;
|
||
switch (pk6.ConsoleRegion)
|
||
{
|
||
case 0: // Japan
|
||
valid = pk6.Country == 1;
|
||
break;
|
||
case 1: // Americas
|
||
valid = 8 <= pk6.Country && pk6.Country <= 52 || new[] {153, 156, 168, 174, 186}.Contains(pk6.Country);
|
||
break;
|
||
case 2: // Europe
|
||
valid = 64 <= pk6.Country && pk6.Country <= 127 || new[] {169, 184, 185}.Contains(pk6.Country);
|
||
break;
|
||
case 4: // China
|
||
valid = pk6.Country == 144 || pk6.Country == 160;
|
||
break;
|
||
case 5: // Korea
|
||
valid = pk6.Country == 136;
|
||
break;
|
||
case 6: // Taiwan
|
||
valid = pk6.Country == 128;
|
||
break;
|
||
}
|
||
return !valid
|
||
? new LegalityCheck(Severity.Invalid, "Geolocation: Country is not in 3DS region.")
|
||
: new LegalityCheck(Severity.Valid, "Geolocation: Country is in 3DS region.");
|
||
}
|
||
private LegalityCheck verifyForm()
|
||
{
|
||
if (!Encounter.Valid)
|
||
return new LegalityCheck(Severity.Valid, "Skipped Form check due to other check being invalid.");
|
||
|
||
switch (pk6.Species)
|
||
{
|
||
case 25:
|
||
if (pk6.AltForm != 0 ^ EncounterType == typeof(EncounterStatic))
|
||
return EncounterType == typeof(EncounterStatic)
|
||
? new LegalityCheck(Severity.Invalid, "Cosplay Pikachu cannot have the default form.")
|
||
: new LegalityCheck(Severity.Invalid, "Only Cosplay Pikachu can have this form.");
|
||
break;
|
||
case 664:
|
||
case 665:
|
||
if (pk6.AltForm > 17) // Fancy & Pokéball
|
||
return new LegalityCheck(Severity.Invalid, "Event Vivillon pattern on pre-evolution.");
|
||
break;
|
||
case 666:
|
||
if (pk6.AltForm > 17) // Fancy & Pokéball
|
||
return EncounterType != typeof (WC6)
|
||
? new LegalityCheck(Severity.Invalid, "Invalid Vivillon pattern.")
|
||
: new LegalityCheck(Severity.Valid, "Valid Vivillon pattern.");
|
||
break;
|
||
case 670:
|
||
if (pk6.AltForm == 5) // Eternal Flower
|
||
return EncounterType != typeof (WC6)
|
||
? new LegalityCheck(Severity.Invalid, "Invalid Eternal Flower encounter.")
|
||
: new LegalityCheck(Severity.Valid, "Valid Eternal Flower encounter.");
|
||
break;
|
||
}
|
||
|
||
return pk6.AltForm > 0 && new[] {Legal.BattleForms, Legal.BattleMegas, Legal.BattlePrimals}.Any(arr => arr.Contains(pk6.Species))
|
||
? new LegalityCheck(Severity.Invalid, "Form cannot exist outside of a battle.")
|
||
: new LegalityCheck();
|
||
}
|
||
private LegalityCheck verifyMisc()
|
||
{
|
||
if (pk6.Gen6 && Encounter.Valid && EncounterType == typeof(WC6) ^ pk6.FatefulEncounter)
|
||
{
|
||
if (EncounterType == typeof(EncounterStatic) && pk6.Species == 386) // Deoxys Matched @ Sky Pillar
|
||
return new LegalityCheck();
|
||
return new LegalityCheck(Severity.Invalid, "Fateful Encounter should " + (pk6.FatefulEncounter ? "not " : "") + "be checked.");
|
||
}
|
||
|
||
return new LegalityCheck();
|
||
}
|
||
private LegalityCheck[] verifyMoves()
|
||
{
|
||
int[] Moves = pk6.Moves;
|
||
LegalityCheck[] res = new LegalityCheck[4];
|
||
for (int i = 0; i < 4; i++)
|
||
res[i] = new LegalityCheck();
|
||
if (!pk6.Gen6)
|
||
return res;
|
||
|
||
var validMoves = Legal.getValidMoves(pk6).ToArray();
|
||
if (pk6.Species == 235)
|
||
{
|
||
for (int i = 0; i < 4; i++)
|
||
res[i] = Legal.InvalidSketch.Contains(Moves[i])
|
||
? new LegalityCheck(Severity.Invalid, "Invalid Sketch move.")
|
||
: new LegalityCheck();
|
||
}
|
||
else if (CardMatch?.Count > 1) // Multiple possible WC6 matched
|
||
{
|
||
int[] RelearnMoves = pk6.RelearnMoves;
|
||
foreach (var wc in CardMatch)
|
||
{
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
if (Moves[i] == Legal.Struggle)
|
||
res[i] = new LegalityCheck(Severity.Invalid, "Invalid Move: Struggle.");
|
||
else if (validMoves.Contains(Moves[i]))
|
||
res[i] = new LegalityCheck(Severity.Valid, Moves[i] == 0 ? "Empty" : "Level-up.");
|
||
else if (RelearnMoves.Contains(Moves[i]))
|
||
res[i] = new LegalityCheck(Severity.Valid, Moves[i] == 0 ? "Empty" : "Relearn Move.") { Flag = true };
|
||
else if (wc.Moves.Contains(Moves[i]))
|
||
res[i] = new LegalityCheck(Severity.Valid, "Wonder Card Non-Relearn Move.");
|
||
else
|
||
res[i] = new LegalityCheck(Severity.Invalid, "Invalid Move.");
|
||
}
|
||
if (res.Any(r => !r.Valid))
|
||
continue;
|
||
|
||
EncounterMatch = wc;
|
||
RelearnBase = wc.RelearnMoves;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int[] RelearnMoves = pk6.RelearnMoves;
|
||
WC6 MatchedWC6 = EncounterMatch as WC6;
|
||
int[] WC6Moves = MatchedWC6?.Moves ?? new int[0];
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
if (Moves[i] == Legal.Struggle)
|
||
res[i] = new LegalityCheck(Severity.Invalid, "Invalid Move: Struggle.");
|
||
else if (validMoves.Contains(Moves[i]))
|
||
res[i] = new LegalityCheck(Severity.Valid, Moves[i] == 0 ? "Empty" : "Level-up.");
|
||
else if (RelearnMoves.Contains(Moves[i]))
|
||
res[i] = new LegalityCheck(Severity.Valid, Moves[i] == 0 ? "Empty" : "Relearn Move.") { Flag = true };
|
||
else if (WC6Moves.Contains(Moves[i]))
|
||
res[i] = new LegalityCheck(Severity.Valid, "Wonder Card Non-Relearn Move.");
|
||
else
|
||
res[i] = new LegalityCheck(Severity.Invalid, "Invalid Move.");
|
||
}
|
||
}
|
||
if (Moves[0] == 0)
|
||
res[0] = new LegalityCheck(Severity.Invalid, "Invalid Move.");
|
||
|
||
if (pk6.Species == 647) // Keldeo
|
||
if (pk6.AltForm == 1 ^ pk6.Moves.Contains(548))
|
||
res[0] = new LegalityCheck(Severity.Invalid, "Secret Sword / Resolute Keldeo Mismatch.");
|
||
|
||
// Duplicate Moves Check
|
||
for (int i = 0; i < 4; i++)
|
||
if (Moves.Count(m => m != 0 && m == Moves[i]) > 1)
|
||
res[i] = new LegalityCheck(Severity.Invalid, "Duplicate Move.");
|
||
|
||
return res;
|
||
}
|
||
private LegalityCheck[] verifyRelearn()
|
||
{
|
||
RelearnBase = null;
|
||
LegalityCheck[] res = new LegalityCheck[4];
|
||
|
||
int[] Moves = pk6.RelearnMoves;
|
||
if (!pk6.Gen6)
|
||
goto noRelearn;
|
||
if (pk6.WasLink)
|
||
{
|
||
var Link = Legal.getValidLinkGifts(pk6);
|
||
if (Link == null)
|
||
{
|
||
for (int i = 0; i < 4; i++)
|
||
res[i] = new LegalityCheck();
|
||
return res;
|
||
}
|
||
EncounterMatch = Link;
|
||
|
||
int[] moves = ((EncounterLink)EncounterMatch).RelearnMoves;
|
||
RelearnBase = moves;
|
||
for (int i = 0; i < 4; i++)
|
||
res[i] = moves[i] != Moves[i]
|
||
? new LegalityCheck(Severity.Invalid, $"Expected: {movelist[moves[i]]}.")
|
||
: new LegalityCheck();
|
||
return res;
|
||
}
|
||
if (pk6.WasEvent || pk6.WasEventEgg)
|
||
{
|
||
// Get WC6's that match
|
||
CardMatch = new List<WC6>(Legal.getValidWC6s(pk6));
|
||
foreach (var wc in CardMatch.ToArray())
|
||
{
|
||
int[] moves = wc.RelearnMoves;
|
||
for (int i = 0; i < 4; i++)
|
||
res[i] = moves[i] != Moves[i]
|
||
? new LegalityCheck(Severity.Invalid, $"Expected ID: {movelist[moves[i]]}.")
|
||
: new LegalityCheck(Severity.Valid, $"Matched WC #{wc.CardID.ToString("0000")}");
|
||
if (res.Any(r => !r.Valid))
|
||
CardMatch.Remove(wc);
|
||
}
|
||
if (CardMatch.Count > 1)
|
||
return res;
|
||
if (CardMatch.Count == 1)
|
||
{ EncounterMatch = CardMatch[0]; RelearnBase = CardMatch[0].RelearnMoves; return res; }
|
||
|
||
EncounterMatch = EncounterType = null;
|
||
goto noRelearn; // No WC match
|
||
}
|
||
|
||
if (pk6.WasEgg && !Legal.NoHatchFromEgg.Contains(pk6.Species))
|
||
{
|
||
const int games = 2;
|
||
bool checkAllGames = pk6.WasTradedEgg;
|
||
bool splitBreed = Legal.SplitBreed.Contains(pk6.Species);
|
||
|
||
int iterate = (checkAllGames ? games : 1) * (splitBreed ? 2 : 1);
|
||
for (int i = 0; i < iterate; i++)
|
||
{
|
||
int gameSource = !checkAllGames ? -1 : i % iterate / (splitBreed ? 2 : 1);
|
||
int skipOption = splitBreed && iterate / 2 <= i ? 1 : 0;
|
||
|
||
// Obtain level1 moves
|
||
List<int> baseMoves = new List<int>(Legal.getBaseEggMoves(pk6, skipOption, gameSource));
|
||
int baseCt = baseMoves.Count;
|
||
if (baseCt > 4) baseCt = 4;
|
||
|
||
// Obtain Nonstandard moves
|
||
var relearnMoves = Legal.getValidRelearn(pk6, skipOption).ToArray();
|
||
var relearn = pk6.RelearnMoves.Where(move => move != 0
|
||
&& (!baseMoves.Contains(move) || relearnMoves.Contains(move))
|
||
).ToArray();
|
||
int relearnCt = relearn.Length;
|
||
|
||
// Get Move Window
|
||
List<int> window = new List<int>(baseMoves);
|
||
window.AddRange(relearn);
|
||
int[] moves = window.Skip(baseCt + relearnCt - 4).Take(4).ToArray();
|
||
Array.Resize(ref moves, 4);
|
||
|
||
int req;
|
||
if (relearnCt == 4)
|
||
req = 0;
|
||
else if (baseCt + relearnCt > 4)
|
||
req = 4 - relearnCt;
|
||
else
|
||
req = baseCt;
|
||
|
||
// Movepool finalized! Check validity.
|
||
|
||
int[] rl = pk6.RelearnMoves;
|
||
string em = string.Join(", ", baseMoves.Select(r => r >= movelist.Length ? "ERROR" : movelist[r]));
|
||
RelearnBase = baseMoves.ToArray();
|
||
// Base Egg Move
|
||
for (int j = 0; j < req; j++)
|
||
{
|
||
if (baseMoves.Contains(rl[j]))
|
||
res[j] = new LegalityCheck(Severity.Valid, "Base egg move.");
|
||
else
|
||
{
|
||
res[j] = new LegalityCheck(Severity.Invalid, "Base egg move missing.");
|
||
for (int f = j+1; f < req; f++)
|
||
res[f] = new LegalityCheck(Severity.Invalid, "Base egg move missing.");
|
||
res[req-1].Comment += $"{Environment.NewLine}Expected the following Relearn Moves: {em}.";
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Non-Base
|
||
if (Legal.LightBall.Contains(pk6.Species))
|
||
relearnMoves = relearnMoves.Concat(new[] { 344 }).ToArray();
|
||
for (int j = req; j < 4; j++)
|
||
res[j] = !relearnMoves.Contains(rl[j])
|
||
? new LegalityCheck(Severity.Invalid, "Not an expected relearn move.")
|
||
: new LegalityCheck(Severity.Valid, rl[j] == 0 ? "Empty" : "Relearn move.");
|
||
|
||
if (res.All(r => r.Valid))
|
||
break;
|
||
}
|
||
return res;
|
||
}
|
||
if (Moves[0] != 0) // DexNav only?
|
||
{
|
||
// Check DexNav
|
||
if (!Legal.getDexNavValid(pk6))
|
||
goto noRelearn;
|
||
|
||
res[0] = !Legal.getValidRelearn(pk6, 0).Contains(Moves[0])
|
||
? new LegalityCheck(Severity.Invalid, "Not an expected DexNav move.")
|
||
: new LegalityCheck();
|
||
for (int i = 1; i < 4; i++)
|
||
res[i] = Moves[i] != 0
|
||
? new LegalityCheck(Severity.Invalid, "Expected no Relearn Move in slot.")
|
||
: new LegalityCheck();
|
||
|
||
if (res[0].Valid)
|
||
RelearnBase = new[] { Moves[0], 0, 0, 0 };
|
||
return res;
|
||
}
|
||
|
||
// Should have no relearn moves.
|
||
noRelearn:
|
||
for (int i = 0; i < 4; i++)
|
||
res[i] = Moves[i] != 0
|
||
? new LegalityCheck(Severity.Invalid, "Expected no Relearn Moves.")
|
||
: new LegalityCheck();
|
||
return res;
|
||
}
|
||
|
||
internal static string[] movelist = Util.getStringList("moves", "en");
|
||
private static readonly string[] EventRibName =
|
||
{
|
||
"Country", "National", "Earth", "World", "Classic",
|
||
"Premier", "Event", "Birthday", "Special", "Souvenir",
|
||
"Wishing", "Battle Champ", "Regional Champ", "National Champ", "World Champ"
|
||
};
|
||
}
|
||
}
|