mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-15 00:37:11 +00:00
Refactoring
relocate ribbon checks/class to more focused location reduce amount of GenNumber checks (use stored Generation value instead)
This commit is contained in:
parent
cf61333b40
commit
0b4e6a0733
9 changed files with 428 additions and 413 deletions
|
@ -2,13 +2,6 @@
|
|||
{
|
||||
public enum GameVersion
|
||||
{
|
||||
// Not actually stored values, but assigned as properties.
|
||||
XD = -11,
|
||||
COLO = -10,
|
||||
BATREV = -7,
|
||||
RSBOX = -5,
|
||||
GS = -4,
|
||||
|
||||
// Indicators
|
||||
Invalid = -2,
|
||||
Any = -1,
|
||||
|
@ -22,23 +15,18 @@
|
|||
/*Gen7*/ SN = 30, MN = 31, US = 32, UM = 33,
|
||||
/* GO */ GO = 34,
|
||||
/* VC1*/ RD = 35, GN = 36, BU = 37, YW = 38, // GN = Blue for international release
|
||||
/* VC2*/ GD = 39, SV = 40, C,
|
||||
/* VC2*/ GD = 39, SV = 40, C = 41, // Crystal is unused
|
||||
|
||||
// Not actually stored values, but assigned as properties.
|
||||
|
||||
// Game Groupings (SaveFile type)
|
||||
RB = 97,
|
||||
RBY = 98,
|
||||
GSC = 99,
|
||||
RS = 100,
|
||||
FRLG = 101,
|
||||
DP = 102,
|
||||
HGSS = 103,
|
||||
BW = 104,
|
||||
B2W2 = 105,
|
||||
XY = 106,
|
||||
ORASDEMO = 107,
|
||||
ORAS = 108,
|
||||
SM = 109,
|
||||
USUM = 110,
|
||||
/*SAV1*/ RB, RBY,
|
||||
/*SAV2*/ GS, GSC,
|
||||
/*SAV3*/ RS, FRLG, RSBOX, COLO, XD,
|
||||
/*SAV4*/ DP, HGSS, BATREV,
|
||||
/*SAV5*/ BW, B2W2,
|
||||
/*SAV6*/ XY, ORASDEMO, ORAS,
|
||||
/*SAV7*/ SM, USUM,
|
||||
|
||||
// Extra Game Groupings (Generation)
|
||||
Gen1, Gen2, Gen3, Gen4, Gen5, Gen6, Gen7,
|
||||
|
@ -68,7 +56,7 @@
|
|||
case GameVersion.Stadium:
|
||||
case GameVersion.EventsGBGen1:
|
||||
case GameVersion.VCEvents:
|
||||
return GameVersion.RBY.Contains(g2);
|
||||
goto case GameVersion.RBY;
|
||||
|
||||
case GameVersion.GS: return g2 == GameVersion.GD || g2 == GameVersion.SV;
|
||||
case GameVersion.GSC:
|
||||
|
@ -77,7 +65,7 @@
|
|||
return GameVersion.GSC.Contains(g2) || g2 == GameVersion.Stadium2 || g2 == GameVersion.EventsGBGen2;
|
||||
case GameVersion.Stadium2:
|
||||
case GameVersion.EventsGBGen2:
|
||||
return GameVersion.GSC.Contains(g2);
|
||||
goto case GameVersion.GSC;
|
||||
case GameVersion.GBCartEraOnly:
|
||||
return g2 == GameVersion.Stadium || g2 == GameVersion.Stadium2 || g2 == GameVersion.EventsGBGen1 || g2 == GameVersion.EventsGBGen2;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace PKHeX.Core
|
|||
AddLine(Severity.Invalid, V203, CheckIdentifier.Gender);
|
||||
|
||||
// Check for PID relationship to Gender & Nature if applicable
|
||||
int gen = pkm.GenNumber;
|
||||
int gen = Info.Generation;
|
||||
|
||||
bool PIDGender = 3 <= gen && gen <= 5;
|
||||
if (!PIDGender)
|
||||
|
@ -77,7 +77,7 @@ namespace PKHeX.Core
|
|||
if (pkm.PID == 0)
|
||||
AddLine(Severity.Fishy, V207, CheckIdentifier.PID);
|
||||
|
||||
if (pkm.GenNumber >= 6 && pkm.PID == pkm.EncryptionConstant)
|
||||
if (Info.Generation >= 6 && pkm.PID == pkm.EncryptionConstant)
|
||||
AddLine(Severity.Invalid, V208, CheckIdentifier.PID); // better to flag than 1:2^32 odds since RNG is not feasible to yield match
|
||||
|
||||
switch (EncounterMatch)
|
||||
|
@ -87,7 +87,7 @@ namespace PKHeX.Core
|
|||
AddLine(Severity.Invalid, V209, CheckIdentifier.Shiny);
|
||||
|
||||
// gen5 correlation
|
||||
if (pkm.GenNumber != 5)
|
||||
if (Info.Generation != 5)
|
||||
break;
|
||||
if (s.Location == 75) // Entree Forest
|
||||
break;
|
||||
|
@ -100,7 +100,7 @@ namespace PKHeX.Core
|
|||
case EncounterSlot w:
|
||||
if (pkm.IsShiny && w.Type == SlotType.HiddenGrotto)
|
||||
AddLine(Severity.Invalid, V221, CheckIdentifier.Shiny);
|
||||
if (pkm.GenNumber == 5 && w.Type != SlotType.HiddenGrotto)
|
||||
if (Info.Generation == 5 && w.Type != SlotType.HiddenGrotto)
|
||||
VerifyG5PID_IDCorrelation();
|
||||
break;
|
||||
case PCD d: // fixed PID
|
||||
|
@ -129,7 +129,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (pkm.EncryptionConstant == 0)
|
||||
AddLine(Severity.Fishy, V201, CheckIdentifier.EC);
|
||||
if (3 <= pkm.GenNumber && pkm.GenNumber <= 5)
|
||||
if (3 <= Info.Generation && Info.Generation <= 5)
|
||||
VerifyTransferEC();
|
||||
else
|
||||
{
|
||||
|
@ -282,7 +282,7 @@ namespace PKHeX.Core
|
|||
|
||||
if (pkm.Format == 2 && pkm.IsEgg && !PKX.IsNicknamedAnyLanguage(0, pkm.Nickname, 2))
|
||||
AddLine(Severity.Valid, V14, CheckIdentifier.Egg);
|
||||
else if (PKX.GetSpeciesNameGeneration(0, pkm.Language, pkm.GenNumber) != pkm.Nickname)
|
||||
else if (PKX.GetSpeciesNameGeneration(0, pkm.Language, Info.Generation) != pkm.Nickname)
|
||||
AddLine(Severity.Invalid, V13, CheckIdentifier.Egg);
|
||||
else
|
||||
AddLine(Severity.Valid, V14, CheckIdentifier.Egg);
|
||||
|
@ -321,7 +321,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
return;
|
||||
}
|
||||
else if (3 <= pkm.GenNumber && pkm.GenNumber <= 5)
|
||||
else if (3 <= Info.Generation && Info.Generation <= 5)
|
||||
{
|
||||
// Suppressing temporarily
|
||||
return;
|
||||
|
@ -702,7 +702,7 @@ namespace PKHeX.Core
|
|||
|
||||
if (pkm.IsEgg && TrainCount > 0)
|
||||
AddLine(Severity.Invalid, V89, CheckIdentifier.Training);
|
||||
else if (TrainCount > 0 && pkm.GenNumber > 6)
|
||||
else if (TrainCount > 0 && Info.Generation > 6)
|
||||
AddLine(Severity.Invalid, V90, CheckIdentifier.Training);
|
||||
else
|
||||
{
|
||||
|
@ -736,7 +736,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
if (pkm.IsEgg && TrainCount > 0)
|
||||
{ AddLine(Severity.Invalid, V89, CheckIdentifier.Training); }
|
||||
else if (TrainCount > 0 && pkm.GenNumber > 6)
|
||||
else if (TrainCount > 0 && Info.Generation > 6)
|
||||
{ AddLine(Severity.Invalid, V90, CheckIdentifier.Training); }
|
||||
else if (TrainCount > 0)
|
||||
{ AddLine(Severity.Fishy, V94, CheckIdentifier.Training); }
|
||||
|
@ -749,263 +749,17 @@ namespace PKHeX.Core
|
|||
|
||||
// Check Unobtainable Ribbons
|
||||
var encounterContent = (EncounterMatch as MysteryGift)?.Content ?? EncounterMatch;
|
||||
List<string> missingRibbons = new List<string>();
|
||||
List<string> invalidRibbons = new List<string>();
|
||||
|
||||
if (pkm.IsEgg)
|
||||
{
|
||||
VerifyRibbonsEgg(encounterContent);
|
||||
return;
|
||||
}
|
||||
|
||||
var ribs = GetRibbonResults(pkm)
|
||||
.Concat(VerifyRibbonSet1(pkm, encounterContent))
|
||||
.Concat(VerifyRibbonSet2(pkm, encounterContent));
|
||||
foreach (var bad in ribs)
|
||||
(bad.Invalid ? invalidRibbons : missingRibbons).Add(bad.Name);
|
||||
|
||||
var result = GetRibbonMessage(missingRibbons, invalidRibbons);
|
||||
if (result.Count == 0)
|
||||
{
|
||||
List<string> result = RibbonVerifier.GetIncorrectRibbons(pkm, encounterContent, Info.Generation);
|
||||
if (result.Any())
|
||||
AddLine(Severity.Invalid, string.Join(Environment.NewLine, result.Where(s => !string.IsNullOrEmpty(s))), CheckIdentifier.Ribbon);
|
||||
else
|
||||
AddLine(Severity.Valid, V602, CheckIdentifier.Ribbon);
|
||||
return;
|
||||
}
|
||||
AddLine(Severity.Invalid, string.Join(Environment.NewLine, result.Where(s => !string.IsNullOrEmpty(s))), CheckIdentifier.Ribbon);
|
||||
}
|
||||
private static List<string> GetRibbonMessage(IReadOnlyCollection<string> missingRibbons, IReadOnlyCollection<string> invalidRibbons)
|
||||
{
|
||||
var result = new List<string>();
|
||||
if (missingRibbons.Count > 0)
|
||||
result.Add(string.Format(V600, string.Join(", ", missingRibbons.Select(z => z.Replace("Ribbon", "")))));
|
||||
if (invalidRibbons.Count > 0)
|
||||
result.Add(string.Format(V601, string.Join(", ", invalidRibbons.Select(z => z.Replace("Ribbon", "")))));
|
||||
return result;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonResults(PKM pkm)
|
||||
{
|
||||
int gen = pkm.GenNumber;
|
||||
|
||||
bool artist = false;
|
||||
if (pkm is IRibbonSetOnly3 o3)
|
||||
{
|
||||
artist = o3.RibbonCounts().Any(z => z == 4);
|
||||
}
|
||||
if (pkm is IRibbonSetUnique3 u3)
|
||||
{
|
||||
if (gen != 3 || !IsAllowedBattleFrontier(pkm.Species))
|
||||
{
|
||||
if (u3.RibbonWinning)
|
||||
yield return new RibbonResult(nameof(u3.RibbonWinning));
|
||||
if (u3.RibbonVictory)
|
||||
yield return new RibbonResult(nameof(u3.RibbonVictory));
|
||||
}
|
||||
}
|
||||
if (pkm is IRibbonSetUnique4 u4)
|
||||
{
|
||||
if (!IsAllowedBattleFrontier(pkm.Species, pkm.AltForm, 4))
|
||||
foreach (var z in GetRibbonMessageNone(u4.RibbonBitsAbility(), u4.RibbonNamesAbility()))
|
||||
yield return z;
|
||||
|
||||
var c3 = u4.RibbonBitsContest3(); var c3n = u4.RibbonNamesContest3();
|
||||
var c4 = u4.RibbonBitsContest4(); var c4n = u4.RibbonNamesContest4();
|
||||
var iter3 = gen == 3 ? getMissingContestRibbons(c3, c3n) : GetRibbonMessageNone(c3, c3n);
|
||||
var iter4 = (gen == 3 || gen == 4) && IsAllowedInContest4(pkm.Species) ? getMissingContestRibbons(c4, c4n) : GetRibbonMessageNone(c4, c4n);
|
||||
foreach (var z in iter3.Concat(iter4))
|
||||
yield return z;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
artist |= c3[3 | i << 2]; // any master rank ribbon
|
||||
|
||||
IEnumerable<RibbonResult> getMissingContestRibbons(IReadOnlyList<bool> bits, IReadOnlyList<string> names)
|
||||
{
|
||||
for (int i = 0; i < bits.Count; i += 4)
|
||||
{
|
||||
bool required = false;
|
||||
for (int j = i + 3; j >= i; j--)
|
||||
if (bits[j])
|
||||
required = true;
|
||||
else if (required)
|
||||
yield return new RibbonResult(names[j], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pkm is IRibbonSetCommon4 s4)
|
||||
{
|
||||
bool inhabited4 = 3 <= gen && gen <= 4;
|
||||
IEnumerable<RibbonResult> iterate = GetRibbonMessage4Any(pkm, s4, gen);
|
||||
if (!inhabited4)
|
||||
iterate = iterate.Concat(GetRibbonMessageNone(s4.RibbonBitsOnly(), s4.RibbonNamesOnly()));
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
}
|
||||
if (pkm is IRibbonSetCommon6 s6)
|
||||
{
|
||||
artist = s6.RibbonCountMemoryContest > 4;
|
||||
bool inhabited6 = 3 <= gen && gen <= 6;
|
||||
var iterate = inhabited6 ? GetRibbonMessage6Any(pkm, s6, gen) : GetRibbonMessageNone(s6.RibbonBits(), s6.RibbonNamesBool());
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
|
||||
if (!inhabited6)
|
||||
{
|
||||
if (s6.RibbonCountMemoryContest > 0)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryContest));
|
||||
if (s6.RibbonCountMemoryBattle > 0)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryBattle));
|
||||
}
|
||||
|
||||
if (s6.RibbonBestFriends && pkm.OT_Affection < 255 && pkm.IsUntraded) // can't lower affection
|
||||
yield return new RibbonResult(nameof(s6.RibbonBestFriends));
|
||||
}
|
||||
if (pkm is IRibbonSetCommon7 s7)
|
||||
{
|
||||
bool inhabited7 = 3 <= gen && gen <= 7 || gen == 1;
|
||||
var iterate = inhabited7 ? GetRibbonMessage7Any(pkm, s7, gen) : GetRibbonMessageNone(s7.RibbonBits(), s7.RibbonNames());
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
}
|
||||
if (pkm is IRibbonSetCommon3 s3)
|
||||
{
|
||||
if (s3.RibbonChampionG3Hoenn && gen != 3)
|
||||
yield return new RibbonResult(nameof(s3.RibbonChampionG3Hoenn)); // RSE HoF
|
||||
if (s3.RibbonArtist && (gen != 3 || !artist))
|
||||
yield return new RibbonResult(nameof(s3.RibbonArtist)); // RSE Master Rank Portrait
|
||||
if (s3.RibbonEffort && gen == 5 && pkm.Format == 5) // unobtainable in Gen 5
|
||||
yield return new RibbonResult(nameof(s3.RibbonEffort));
|
||||
}
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessage4Any(PKM pkm, IRibbonSetCommon4 s4, int gen)
|
||||
{
|
||||
if (s4.RibbonRecord)
|
||||
yield return new RibbonResult(nameof(s4.RibbonRecord)); // Unobtainable
|
||||
if (s4.RibbonFootprint && (pkm.Format < 6 && gen == 5 || gen >= 6 && pkm.CurrentLevel - pkm.Met_Level < 30))
|
||||
yield return new RibbonResult(nameof(s4.RibbonFootprint));
|
||||
|
||||
bool gen34 = gen == 3 || gen == 4;
|
||||
bool not6 = pkm.Format < 6 || gen > 6 || gen < 3;
|
||||
bool noDaily = !gen34 && not6;
|
||||
bool noCosmetic = !gen34 && (not6 || pkm.XY && pkm.IsUntraded);
|
||||
|
||||
if (noDaily)
|
||||
foreach (var z in GetRibbonMessageNone(s4.RibbonBitsDaily(), s4.RibbonNamesDaily()))
|
||||
yield return z;
|
||||
if (noCosmetic)
|
||||
foreach (var z in GetRibbonMessageNone(s4.RibbonBitsCosmetic(), s4.RibbonNamesCosmetic()))
|
||||
yield return z;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessage6Any(PKM pkm, IRibbonSetCommon6 s6, int gen)
|
||||
{
|
||||
foreach (var p in GetRibbonMessage6Memory(pkm, s6, gen))
|
||||
yield return p;
|
||||
|
||||
bool untraded = pkm.IsUntraded;
|
||||
var iter = untraded ? GetRibbonMessage6Untraded(pkm, s6) : GetRibbonMessage6Traded(pkm, s6);
|
||||
foreach (var p in iter)
|
||||
yield return p;
|
||||
|
||||
bool allContest = s6.RibbonBitsContest().All(z => z);
|
||||
if (allContest ^ s6.RibbonContestStar && !(untraded && pkm.XY)) // if not already checked
|
||||
yield return new RibbonResult(nameof(s6.RibbonContestStar), s6.RibbonContestStar);
|
||||
|
||||
const int mem_Chatelaine = 30;
|
||||
bool hasChampMemory = pkm.HT_Memory == mem_Chatelaine || pkm.OT_Memory == mem_Chatelaine;
|
||||
if (!hasChampMemory || s6.RibbonBattlerSkillful || s6.RibbonBattlerExpert)
|
||||
yield break;
|
||||
|
||||
var result = new RibbonResult(nameof(s6.RibbonBattlerSkillful), false);
|
||||
result.Combine(new RibbonResult(nameof(s6.RibbonBattlerExpert)));
|
||||
yield return result;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessage6Memory(PKM pkm, IRibbonSetCommon6 s6, int gen)
|
||||
{
|
||||
int contest = 0;
|
||||
int battle = 0;
|
||||
switch (gen)
|
||||
{
|
||||
case 3:
|
||||
contest = IsAllowedInContest4(pkm.Species) ? 40 : 20;
|
||||
battle = IsAllowedBattleFrontier(pkm.Species) ? 8 : 0;
|
||||
break;
|
||||
case 4:
|
||||
contest = IsAllowedInContest4(pkm.Species) ? 20 : 0;
|
||||
battle = IsAllowedBattleFrontier(pkm.Species) ? 6 : 0;
|
||||
break;
|
||||
}
|
||||
if (s6.RibbonCountMemoryContest > contest)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryContest));
|
||||
if (s6.RibbonCountMemoryBattle > battle)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryBattle));
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessage6Untraded(PKM pkm, IRibbonSetCommon6 s6)
|
||||
{
|
||||
if (pkm.XY)
|
||||
{
|
||||
if (s6.RibbonChampionG6Hoenn)
|
||||
yield return new RibbonResult(nameof(s6.RibbonChampionG6Hoenn));
|
||||
|
||||
if (s6.RibbonContestStar)
|
||||
yield return new RibbonResult(nameof(s6.RibbonContestStar));
|
||||
if (s6.RibbonMasterCoolness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterCoolness));
|
||||
if (s6.RibbonMasterBeauty)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterBeauty));
|
||||
if (s6.RibbonMasterCuteness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterCuteness));
|
||||
if (s6.RibbonMasterCleverness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterCleverness));
|
||||
if (s6.RibbonMasterToughness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterToughness));
|
||||
}
|
||||
else if (pkm.AO)
|
||||
{
|
||||
if (s6.RibbonChampionKalos)
|
||||
yield return new RibbonResult(nameof(s6.RibbonChampionKalos));
|
||||
}
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessage6Traded(PKM pkm, IRibbonSetCommon6 s6)
|
||||
{
|
||||
if (s6.RibbonTraining)
|
||||
{
|
||||
const int req = 12; // only first 12
|
||||
int count = pkm.SuperTrainingMedalCount(req);
|
||||
if (count < req)
|
||||
yield return new RibbonResult(nameof(s6.RibbonTraining));
|
||||
}
|
||||
|
||||
const int mem_Champion = 27;
|
||||
bool hasChampMemory = pkm.HT_Memory == mem_Champion || pkm.OT_Memory == mem_Champion;
|
||||
if (!hasChampMemory || s6.RibbonChampionKalos || s6.RibbonChampionG6Hoenn)
|
||||
yield break;
|
||||
|
||||
var result = new RibbonResult(nameof(s6.RibbonChampionKalos), false);
|
||||
result.Combine(new RibbonResult(nameof(s6.RibbonChampionG6Hoenn)));
|
||||
yield return result;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessage7Any(PKM pkm, IRibbonSetCommon7 s7, int gen)
|
||||
{
|
||||
if (!IsAllowedBattleFrontier(pkm.Species))
|
||||
{
|
||||
if (s7.RibbonBattleRoyale)
|
||||
yield return new RibbonResult(nameof(s7.RibbonBattleRoyale));
|
||||
if (s7.RibbonBattleTreeGreat)
|
||||
yield return new RibbonResult(nameof(s7.RibbonBattleTreeGreat));
|
||||
if (s7.RibbonBattleTreeMaster)
|
||||
yield return new RibbonResult(nameof(s7.RibbonBattleTreeMaster));
|
||||
}
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonMessageNone(IReadOnlyList<bool> bits, IReadOnlyList<string> names)
|
||||
{
|
||||
for (int i = 0; i < bits.Count; i++)
|
||||
if (bits[i])
|
||||
yield return new RibbonResult(names[i]);
|
||||
}
|
||||
private static bool IsAllowedInContest4(int species) => species != 201 && species != 132; // Disallow Unown and Ditto
|
||||
private static bool IsAllowedBattleFrontier(int species, int form = 0, int gen = 0)
|
||||
{
|
||||
if (gen == 4 && species == 172 && form == 1) // spiky
|
||||
return false;
|
||||
|
||||
return !Legal.BattleFrontierBanlist.Contains(species);
|
||||
}
|
||||
private void VerifyRibbonsEgg(object encounter)
|
||||
{
|
||||
|
@ -1029,64 +783,6 @@ namespace PKHeX.Core
|
|||
bool HasCount(object o) => o is int z && z > 0;
|
||||
}
|
||||
}
|
||||
private static IEnumerable<RibbonResult> VerifyRibbonSet1(PKM pkm, object encounterContent)
|
||||
{
|
||||
if (!(pkm is IRibbonSetEvent3 set1))
|
||||
yield break;
|
||||
var names = set1.RibbonNames();
|
||||
var sb = set1.RibbonBits();
|
||||
var eb = (encounterContent as IRibbonSetEvent3).RibbonBits();
|
||||
|
||||
if (pkm.Gen3)
|
||||
{
|
||||
eb[0] = sb[0]; // permit Earth Ribbon
|
||||
if (pkm.Version == 15 && encounterContent is EncounterStaticShadow s)
|
||||
{
|
||||
// only require national ribbon if no longer on origin game
|
||||
bool xd = !Encounters3.Encounter_Colo.Contains(s);
|
||||
eb[1] = !(xd && pkm is XK3 x && !x.RibbonNational || !xd && pkm is CK3 c && !c.RibbonNational);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sb.Length; i++)
|
||||
if (sb[i] != eb[i])
|
||||
yield return new RibbonResult(names[i], !eb[i]); // only flag if invalid
|
||||
}
|
||||
private static IEnumerable<RibbonResult> VerifyRibbonSet2(PKM pkm, object encounterContent)
|
||||
{
|
||||
if (!(pkm is IRibbonSetEvent4 set2))
|
||||
yield break;
|
||||
var names = set2.RibbonNames();
|
||||
var sb = set2.RibbonBits();
|
||||
var eb = (encounterContent as IRibbonSetEvent4).RibbonBits();
|
||||
|
||||
if (encounterContent is EncounterStatic s && s.RibbonWishing)
|
||||
eb[1] = true; // require Wishing Ribbon
|
||||
|
||||
for (int i = 0; i < sb.Length; i++)
|
||||
if (sb[i] != eb[i])
|
||||
yield return new RibbonResult(names[i], !eb[i]); // only flag if invalid
|
||||
}
|
||||
private class RibbonResult
|
||||
{
|
||||
/// <summary>Ribbon Display Name</summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary> Ribbon should not be present. </summary>
|
||||
/// <remarks> If this is false, the Ribbon is missing. </remarks>
|
||||
public bool Invalid { get; }
|
||||
|
||||
public RibbonResult(string prop, bool invalid = true)
|
||||
{
|
||||
Name = RibbonStrings.GetName(prop) ?? prop;
|
||||
Invalid = invalid;
|
||||
}
|
||||
|
||||
public void Combine(RibbonResult other)
|
||||
{
|
||||
Name += " / " + other.Name;
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyCXD()
|
||||
{
|
||||
|
@ -1158,7 +854,7 @@ namespace PKHeX.Core
|
|||
if (EncounterAbility != null && VerifySetAbility(EncounterAbility, AbilityUnchanged, abilities, abilval))
|
||||
return; // result added via VerifySetAbility
|
||||
|
||||
switch (pkm.GenNumber)
|
||||
switch (Info.Generation)
|
||||
{
|
||||
case 5: VerifyAbility5(abilities); break;
|
||||
case 6: VerifyAbility6(abilities); break;
|
||||
|
@ -1166,7 +862,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
if (3 <= pkm.GenNumber && pkm.GenNumber <= 4 && pkm.AbilityNumber == 4)
|
||||
if (3 <= Info.Generation && Info.Generation <= 4 && pkm.AbilityNumber == 4)
|
||||
AddLine(Severity.Invalid, V112, CheckIdentifier.Ability);
|
||||
else if (AbilityUnchanged != null && abilities[pkm.AbilityNumber >> 1] != pkm.Ability)
|
||||
AddLine(Severity.Invalid, pkm.Format < 6 ? V113 : V114, CheckIdentifier.Ability);
|
||||
|
@ -1338,7 +1034,7 @@ namespace PKHeX.Core
|
|||
return;
|
||||
}
|
||||
|
||||
if (pkm.Species == 292 && pkm.GenNumber > 3) // Shedinja. For gen3, copy the ball from Nincada
|
||||
if (pkm.Species == 292 && Info.Generation > 3) // Shedinja. For gen3, copy the ball from Nincada
|
||||
{
|
||||
VerifyBallEquals(4); // Pokeball Only
|
||||
return;
|
||||
|
@ -1371,9 +1067,9 @@ namespace PKHeX.Core
|
|||
// For gen3/4 Safari Zones and BCC getValidWildEncounters already filter to not return
|
||||
// mixed possible encounters between safari, BCC and other encounters
|
||||
// That means is the first encounter is not safari then there is no safari encounter in the array
|
||||
else if (3 <= pkm.GenNumber && pkm.GenNumber <= 4 && EncounterGenerator.IsSafariSlot(w.Type))
|
||||
else if (3 <= Info.Generation && Info.Generation <= 4 && EncounterGenerator.IsSafariSlot(w.Type))
|
||||
VerifyBallEquals(5); // Safari Ball
|
||||
else if (pkm.GenNumber == 4 && w.Type == SlotType.BugContest)
|
||||
else if (Info.Generation == 4 && w.Type == SlotType.BugContest)
|
||||
VerifyBallEquals(0x18); // Sport Ball
|
||||
else
|
||||
VerifyBallEquals(Legal.GetWildBalls(pkm));
|
||||
|
@ -1390,7 +1086,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
private void VerifyBallEgg()
|
||||
{
|
||||
if (pkm.GenNumber < 6) // No inheriting Balls
|
||||
if (Info.Generation < 6) // No inheriting Balls
|
||||
{
|
||||
VerifyBallEquals(4); // Must be Pokéball -- no ball inheritance.
|
||||
return;
|
||||
|
@ -1403,7 +1099,7 @@ namespace PKHeX.Core
|
|||
if (pkm.Ball == 0x04) // Poké Ball
|
||||
{ AddLine(Severity.Valid, V119, CheckIdentifier.Ball); return; }
|
||||
|
||||
switch (pkm.GenNumber)
|
||||
switch (Info.Generation)
|
||||
{
|
||||
case 6: // Gen6 Inheritance Rules
|
||||
VerifyBallEggGen6();
|
||||
|
@ -1615,7 +1311,7 @@ namespace PKHeX.Core
|
|||
if (!Encounter.Valid)
|
||||
return new CheckResult(Severity.Valid, V127, CheckIdentifier.History);
|
||||
|
||||
if (pkm.GenNumber < 6)
|
||||
if (Info.Generation < 6)
|
||||
{
|
||||
if (pkm.Format < 6)
|
||||
return new CheckResult(Severity.Valid, V128, CheckIdentifier.History);
|
||||
|
@ -1626,7 +1322,7 @@ namespace PKHeX.Core
|
|||
return new CheckResult(Severity.Invalid, V130, CheckIdentifier.History);
|
||||
}
|
||||
|
||||
if (pkm.Format >= 6 && pkm.GenNumber != pkm.Format && pkm.CurrentHandler != 1)
|
||||
if (pkm.Format >= 6 && Info.Generation != pkm.Format && pkm.CurrentHandler != 1)
|
||||
return new CheckResult(Severity.Invalid, V124, CheckIdentifier.History);
|
||||
|
||||
if (pkm.HT_Gender > 1)
|
||||
|
@ -1691,7 +1387,7 @@ namespace PKHeX.Core
|
|||
return new CheckResult(Severity.Invalid, V137, CheckIdentifier.History);
|
||||
}
|
||||
|
||||
if (pkm.GenNumber >= 7 && pkm.CNTs.Any(stat => stat > 0))
|
||||
if (Info.Generation >= 7 && pkm.CNTs.Any(stat => stat > 0))
|
||||
return new CheckResult(Severity.Invalid, V138, CheckIdentifier.History);
|
||||
|
||||
if (!pkm.WasEvent && pkm.HT_Name.Length == 0) // Is not Traded
|
||||
|
@ -1729,7 +1425,7 @@ namespace PKHeX.Core
|
|||
|
||||
if (pkm.WasLink && (EncounterMatch as EncounterLink)?.OT == false)
|
||||
untraded = false;
|
||||
else if (pkm.GenNumber < 6)
|
||||
else if (Info.Generation < 6)
|
||||
untraded = false;
|
||||
|
||||
if (untraded) // Is not Traded
|
||||
|
@ -1778,7 +1474,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (pkm.OT_Memory == 0 ^ !pkm.Gen6)
|
||||
return new CheckResult(Severity.Invalid, V152, CheckIdentifier.History);
|
||||
if (pkm.GenNumber < 6 && pkm.OT_Affection != 0)
|
||||
if (Info.Generation < 6 && pkm.OT_Affection != 0)
|
||||
return new CheckResult(Severity.Invalid, V129, CheckIdentifier.History);
|
||||
}
|
||||
// Unimplemented: Ingame Trade Memories
|
||||
|
@ -1842,7 +1538,7 @@ namespace PKHeX.Core
|
|||
if (!History.Valid)
|
||||
return;
|
||||
|
||||
if (pkm.GenNumber < 6)
|
||||
if (Info.Generation < 6)
|
||||
{
|
||||
VerifyOTMemoryIs(new [] {0, 0, 0, 0}); // empty
|
||||
return;
|
||||
|
@ -1872,7 +1568,7 @@ namespace PKHeX.Core
|
|||
VerifyOTMemoryIs(new[] {g.OT_Memory, g.OT_Intensity, g.OT_TextVar, g.OT_Feeling});
|
||||
return;
|
||||
}
|
||||
if (pkm.GenNumber >= 7)
|
||||
if (Info.Generation >= 7)
|
||||
{
|
||||
VerifyOTMemoryIs(new[] {0, 0, 0, 0}); // empty
|
||||
return;
|
||||
|
@ -1901,7 +1597,7 @@ namespace PKHeX.Core
|
|||
return;
|
||||
|
||||
case 14:
|
||||
if (!Legal.GetCanBeCaptured(pkm.OT_TextVar, pkm.GenNumber, (GameVersion)pkm.Version))
|
||||
if (!Legal.GetCanBeCaptured(pkm.OT_TextVar, Info.Generation, (GameVersion)pkm.Version))
|
||||
AddLine(Severity.Invalid, string.Format(V165, V205), CheckIdentifier.Memory);
|
||||
else
|
||||
AddLine(Severity.Valid, string.Format(V164, V205), CheckIdentifier.Memory);
|
||||
|
@ -1937,7 +1633,7 @@ namespace PKHeX.Core
|
|||
|
||||
// Transfer 6->7 & withdraw to same HT => keeps past gen memory
|
||||
// Don't require link trade memory for these past gen cases
|
||||
int gen = pkm.GenNumber;
|
||||
int gen = Info.Generation;
|
||||
if (3 <= gen && gen < 7 && pkm.CurrentHandler == 1)
|
||||
return;
|
||||
|
||||
|
@ -1965,7 +1661,7 @@ namespace PKHeX.Core
|
|||
AddLine(Severity.Invalid, string.Format(V160, V206), CheckIdentifier.Memory); return;
|
||||
|
||||
case 14:
|
||||
if (Legal.GetCanBeCaptured(pkm.HT_TextVar, pkm.GenNumber))
|
||||
if (Legal.GetCanBeCaptured(pkm.HT_TextVar, Info.Generation))
|
||||
AddLine(Severity.Valid, string.Format(V164, V206), CheckIdentifier.Memory);
|
||||
else
|
||||
AddLine(Severity.Invalid, string.Format(V165, V206), CheckIdentifier.Memory);
|
||||
|
@ -2027,7 +1723,7 @@ namespace PKHeX.Core
|
|||
int species = pkm.Species;
|
||||
if (species == 201) // Unown
|
||||
{
|
||||
int maxCount = pkm.GenNumber == 2 ? 26 : 28; // A-Z : A-Z?!
|
||||
int maxCount = Info.Generation == 2 ? 26 : 28; // A-Z : A-Z?!
|
||||
if (pkm.AltForm < maxCount)
|
||||
valid = true;
|
||||
}
|
||||
|
@ -2047,13 +1743,13 @@ namespace PKHeX.Core
|
|||
switch (pkm.Species)
|
||||
{
|
||||
case 25: // Pikachu
|
||||
if (pkm.GenNumber == 6 && pkm.AltForm != 0 ^ Type == typeof(EncounterStatic))
|
||||
if (Info.Generation == 6 && pkm.AltForm != 0 ^ Type == typeof(EncounterStatic))
|
||||
{
|
||||
string msg = Type == typeof(EncounterStatic) ? V305 : V306;
|
||||
AddLine(Severity.Invalid, msg, CheckIdentifier.Form);
|
||||
return;
|
||||
}
|
||||
if (pkm.GenNumber == 7 && pkm.AltForm != 0 ^ Type == typeof(MysteryGift))
|
||||
if (Info.Generation == 7 && pkm.AltForm != 0 ^ Type == typeof(MysteryGift))
|
||||
{
|
||||
if (EncounterMatch is WC7 gift && gift.Form != pkm.AltForm)
|
||||
{
|
||||
|
@ -2191,7 +1887,7 @@ namespace PKHeX.Core
|
|||
break;
|
||||
}
|
||||
|
||||
if (pkm.Format >= 7 && pkm.GenNumber < 7 && pkm.AltForm != 0)
|
||||
if (pkm.Format >= 7 && Info.Generation < 7 && pkm.AltForm != 0)
|
||||
{
|
||||
if (pkm.Species == 25 || Legal.AlolanOriginForms.Contains(pkm.Species))
|
||||
{ AddLine(Severity.Invalid, V317, CheckIdentifier.Form); return; }
|
||||
|
@ -2293,7 +1989,7 @@ namespace PKHeX.Core
|
|||
if (!Encounter.Valid)
|
||||
return;
|
||||
|
||||
if (pkm.GenNumber == 5 && ((EncounterMatch as EncounterStatic)?.NSparkle ?? false))
|
||||
if (Info.Generation == 5 && ((EncounterMatch as EncounterStatic)?.NSparkle ?? false))
|
||||
VerifyNsPKM();
|
||||
|
||||
switch (EncounterMatch)
|
||||
|
@ -2448,7 +2144,8 @@ namespace PKHeX.Core
|
|||
}
|
||||
#region VerifyMoves
|
||||
#endregion
|
||||
public static string[] MoveStrings { get; set; } = Util.GetMovesList("en");
|
||||
public static string[] SpeciesStrings { get; set; } = Util.GetSpeciesList("en");
|
||||
public static string[] MoveStrings { internal get; set; } = Util.GetMovesList("en");
|
||||
public static string[] SpeciesStrings { internal get; set; } = Util.GetSpeciesList("en");
|
||||
internal static IEnumerable<string> getMoveNames(IEnumerable<int> moves) => moves.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m]);
|
||||
}
|
||||
}
|
||||
|
|
21
PKHeX.Core/Legality/Encounters/EncounterRejected.cs
Normal file
21
PKHeX.Core/Legality/Encounters/EncounterRejected.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public class EncounterRejected : IEncounterable
|
||||
{
|
||||
public readonly IEncounterable Encounter;
|
||||
public readonly CheckResult Check;
|
||||
public string Reason => Check.Comment;
|
||||
|
||||
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;
|
||||
|
||||
public EncounterRejected(IEncounterable encounter, CheckResult check)
|
||||
{
|
||||
Encounter = encounter;
|
||||
Check = check;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,10 +8,10 @@ namespace PKHeX.Core
|
|||
/// <summary>The <see cref="PKM"/> object used for comparisons.</summary>
|
||||
private readonly PKM pkm;
|
||||
|
||||
/// <summary>The generation of games the PKM originated from.</summary>
|
||||
/// <summary>The generation of games the <see cref="PKM"/> originated from.</summary>
|
||||
public int Generation { get; set; }
|
||||
|
||||
/// <summary> The Game the PKM originated from.</summary>
|
||||
/// <summary>The Game the <see cref="PKM"/> originated from.</summary>
|
||||
public GameVersion Game { get; set; }
|
||||
|
||||
/// <summary>The matched Encounter details for the <see cref="PKM"/>. </summary>
|
||||
|
@ -26,60 +26,47 @@ namespace PKHeX.Core
|
|||
Parse.Clear();
|
||||
}
|
||||
}
|
||||
private IEncounterable _match;
|
||||
|
||||
/// <summary>Indicates whether or not the <see cref="PKM"/> originated from <see cref="GameVersion.XD"/>.</summary>
|
||||
public bool WasXD => pkm?.Version == 15 && EncounterMatch != null && !Encounters3.Encounter_Colo.Contains(EncounterMatch);
|
||||
|
||||
/// <summary>Base Relearn Moves for the <see cref="EncounterMatch"/>.</summary>
|
||||
public int[] RelearnBase { get; set; }
|
||||
|
||||
/// <summary>Top level Legality Check result list for the <see cref="EncounterMatch"/>.</summary>
|
||||
public readonly List<CheckResult> Parse = new List<CheckResult>();
|
||||
|
||||
public CheckResult[] Relearn { get; set; } = new CheckResult[4];
|
||||
public CheckMoveResult[] Moves { get; set; } = new CheckMoveResult[4];
|
||||
|
||||
public DexLevel[][] EvoChainsAllGens => _evochains ?? (_evochains = Legal.GetEvolutionChainsAllGens(pkm, EncounterMatch));
|
||||
public ValidEncounterMoves EncounterMoves { get; set; }
|
||||
|
||||
public DexLevel[][] EvoChainsAllGens => _evochains ?? (_evochains = Legal.GetEvolutionChainsAllGens(pkm, EncounterMatch));
|
||||
private DexLevel[][] _evochains;
|
||||
private IEncounterable _match;
|
||||
|
||||
/// <summary><see cref="RNG"/> related information that generated the <see cref="PKM.PID"/>/<see cref="PKM.IVs"/> value(s).</summary>
|
||||
public PIDIV PIDIV { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether or not the <see cref="PIDIV"/> can originate from the <see cref="EncounterMatch"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This boolean is true until all valid PIDIV encounters are tested, at which time it is false.</remarks>
|
||||
/// <summary>Indicates whether or not the <see cref="PIDIV"/> can originate from the <see cref="EncounterMatch"/>.</summary>
|
||||
/// <remarks>This boolean is true until all valid <see cref="PIDIV"/> encounters are tested, at which time it is false.</remarks>
|
||||
public bool PIDIVMatches { get; set; } = true;
|
||||
|
||||
public LegalInfo(PKM pk)
|
||||
{
|
||||
pkm = pk;
|
||||
Game = (GameVersion) pkm.Version;
|
||||
|
||||
// Store repeatedly accessed values
|
||||
Game = (GameVersion)pkm.Version;
|
||||
Generation = pkm.GenNumber;
|
||||
}
|
||||
|
||||
/// <summary>List of all near-matches that were rejected for a given reason.</summary>
|
||||
public List<EncounterRejected> InvalidMatches;
|
||||
internal void Reject(CheckResult c)
|
||||
{
|
||||
if (InvalidMatches == null)
|
||||
InvalidMatches = new List<RejectedEncounter>();
|
||||
InvalidMatches.Add(new RejectedEncounter(EncounterMatch, c));
|
||||
InvalidMatches = new List<EncounterRejected>();
|
||||
InvalidMatches.Add(new EncounterRejected(EncounterMatch, c));
|
||||
}
|
||||
|
||||
public class RejectedEncounter : IEncounterable
|
||||
{
|
||||
public readonly IEncounterable Encounter;
|
||||
public readonly CheckResult Check;
|
||||
public string Reason => Check.Comment;
|
||||
|
||||
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;
|
||||
|
||||
public RejectedEncounter(IEncounterable encounter, CheckResult check)
|
||||
{
|
||||
Encounter = encounter;
|
||||
Check = check;
|
||||
}
|
||||
}
|
||||
public List<RejectedEncounter> InvalidMatches;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,12 +27,12 @@ namespace PKHeX.Core
|
|||
return ParseMovesForSmeargle(pkm, Moves, info); // Smeargle can have any moves except a few
|
||||
|
||||
// Iterate over encounters
|
||||
bool pre3DS = pkm.GenNumber < 6;
|
||||
bool pre3DS = info.Generation < 6;
|
||||
|
||||
// gather valid moves for encounter species
|
||||
info.EncounterMoves = new ValidEncounterMoves(pkm, info);
|
||||
|
||||
if (pkm.GenNumber <= 3)
|
||||
if (info.Generation <= 3)
|
||||
pkm.WasEgg = info.EncounterMatch.EggEncounter;
|
||||
|
||||
var EncounterMatchGen = info.EncounterMatch as IGeneration;
|
||||
|
@ -95,7 +95,7 @@ namespace PKHeX.Core
|
|||
NonTradebackLvlMoves = Legal.GetExclusivePreEvolutionMoves(pkm, info.EncounterMatch.Species, info.EvoChainsAllGens[2], 2, e.Game).Where(m => m > Legal.MaxMoveID_1).ToArray();
|
||||
var Egg = Legal.GetEggMoves(pkm, e.Species, pkm.AltForm);
|
||||
|
||||
bool volt = (pkm.GenNumber > 3 || e.Game == GameVersion.E) && Legal.LightBall.Contains(pkm.Species);
|
||||
bool volt = (info.Generation > 3 || e.Game == GameVersion.E) && Legal.LightBall.Contains(pkm.Species);
|
||||
var Special = volt && EventEggMoves.Length == 0 ? new[] { 344 } : new int[0]; // Volt Tackle for bred Pichu line
|
||||
|
||||
var source = new MoveParseSource
|
||||
|
@ -121,7 +121,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
private static CheckMoveResult[] ParseMoves3DS(PKM pkm, int[] Moves, LegalInfo info)
|
||||
{
|
||||
info.EncounterMoves.Relearn = pkm.GenNumber >= 6 ? pkm.RelearnMoves : new int[0];
|
||||
info.EncounterMoves.Relearn = info.Generation >= 6 ? pkm.RelearnMoves : new int[0];
|
||||
if (info.EncounterMatch is IMoveset)
|
||||
return ParseMovesSpecialMoveset(pkm, Moves, info);
|
||||
|
||||
|
@ -136,7 +136,7 @@ namespace PKHeX.Core
|
|||
return ParseMovesIsEggPreRelearn(pkm, Moves, SpecialMoves, egg);
|
||||
}
|
||||
var NoMoveReminder = (info.EncounterMatch as IGeneration)?.Generation == 1 || (info.EncounterMatch as IGeneration)?.Generation == 2 && !Legal.AllowGen2MoveReminder;
|
||||
if (pkm.GenNumber <= 2 && NoMoveReminder)
|
||||
if (info.Generation <= 2 && NoMoveReminder)
|
||||
return ParseMovesGenGB(pkm, Moves, info);
|
||||
if (info.EncounterMatch is EncounterEgg e)
|
||||
return ParseMovesWasEggPreRelearn(pkm, Moves, info, e);
|
||||
|
@ -234,7 +234,7 @@ namespace PKHeX.Core
|
|||
if (source.CurrentMoves[m] == 0)
|
||||
res[m] = new CheckMoveResult(MoveSource.None, pkm.Format, m < required ? Severity.Fishy : Severity.Valid, V167, CheckIdentifier.Move);
|
||||
else if (info.EncounterMoves.Relearn.Contains(source.CurrentMoves[m]))
|
||||
res[m] = new CheckMoveResult(MoveSource.Relearn, pkm.GenNumber, Severity.Valid, V172, CheckIdentifier.Move) { Flag = true };
|
||||
res[m] = new CheckMoveResult(MoveSource.Relearn, info.Generation, Severity.Valid, V172, CheckIdentifier.Move) { Flag = true };
|
||||
}
|
||||
|
||||
if (AllParsed())
|
||||
|
@ -266,7 +266,7 @@ namespace PKHeX.Core
|
|||
for (int m = 0; m < 4; m++)
|
||||
{
|
||||
if (res[m] == null)
|
||||
res[m] = new CheckMoveResult(MoveSource.Unknown, pkm.GenNumber, Severity.Invalid, V176, CheckIdentifier.Move);
|
||||
res[m] = new CheckMoveResult(MoveSource.Unknown, info.Generation, Severity.Invalid, V176, CheckIdentifier.Move);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -317,7 +317,7 @@ namespace PKHeX.Core
|
|||
res[m] = new CheckMoveResult(MoveSource.TMHM, gen, Severity.Valid, native ? V173 : string.Format(V331, gen), CheckIdentifier.Move);
|
||||
else if (info.EncounterMoves.TutorMoves[gen].Contains(moves[m]))
|
||||
res[m] = new CheckMoveResult(MoveSource.Tutor, gen, Severity.Valid, native ? V174 : string.Format(V332, gen), CheckIdentifier.Move);
|
||||
else if (gen == pkm.GenNumber && learnInfo.Source.SpecialSource.Contains(moves[m]))
|
||||
else if (gen == info.Generation && learnInfo.Source.SpecialSource.Contains(moves[m]))
|
||||
res[m] = new CheckMoveResult(MoveSource.Special, gen, Severity.Valid, V175, CheckIdentifier.Move);
|
||||
|
||||
if (res[m] == null || gen >= 3)
|
||||
|
@ -332,7 +332,7 @@ namespace PKHeX.Core
|
|||
learnInfo.MixedGen12NonTradeback = true;
|
||||
}
|
||||
|
||||
if (res[m].Valid && gen <= 2 && pkm.TradebackStatus == TradebackType.Any && pkm.GenNumber != gen)
|
||||
if (res[m].Valid && gen <= 2 && pkm.TradebackStatus == TradebackType.Any && info.Generation != gen)
|
||||
pkm.TradebackStatus = TradebackType.WasTradeback;
|
||||
}
|
||||
}
|
||||
|
@ -699,7 +699,7 @@ namespace PKHeX.Core
|
|||
res[z] = new CheckMoveResult(MoveSource.Initial, gen, Severity.Invalid, V180, CheckIdentifier.Move);
|
||||
|
||||
// provide the list of suggested base moves for the last required slot
|
||||
em = string.Join(", ", infoset.Base.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m]));
|
||||
em = string.Join(", ", getMoveNames(infoset.Base));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -719,10 +719,10 @@ namespace PKHeX.Core
|
|||
res[z] = new CheckMoveResult(MoveSource.SpecialEgg, gen, 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(", ", infoset.Base.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m])) + ", ";
|
||||
em += string.Join(", ", infoset.Special.Select(m => m >= MoveStrings.Length ? V190 : MoveStrings[m]));
|
||||
if (string.IsNullOrEmpty(em))
|
||||
em = string.Join(", ", getMoveNames(infoset.Base));
|
||||
em += ", ";
|
||||
em += string.Join(", ", getMoveNames(infoset.Special));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static CheckResult[] VerifyRelearn(PKM pkm, LegalInfo info)
|
||||
{
|
||||
if (pkm.GenNumber < 6 || pkm.VC1)
|
||||
if (info.Generation < 6 || pkm.VC1)
|
||||
return VerifyRelearnNone(pkm, info);
|
||||
|
||||
if (info.EncounterMatch is EncounterLink l)
|
||||
|
@ -143,7 +143,7 @@ namespace PKHeX.Core
|
|||
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 >= MoveStrings.Length ? V190 : MoveStrings[m]));
|
||||
string em = string.Join(", ", getMoveNames(baseMoves));
|
||||
res[required - 1].Comment += string.Format(Environment.NewLine + V181, em);
|
||||
}
|
||||
private static bool FlagInvalidInheritedMoves(CheckResult[] res, int required, EncounterEgg e, IReadOnlyList<int> RelearnMoves, IReadOnlyList<int> inheritMoves, IReadOnlyList<int> splitMoves)
|
||||
|
|
23
PKHeX.Core/Legality/Ribbons/RibbonResult.cs
Normal file
23
PKHeX.Core/Legality/Ribbons/RibbonResult.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
internal class RibbonResult
|
||||
{
|
||||
/// <summary>Ribbon Display Name</summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>Ribbon should not be present.</summary>
|
||||
/// <remarks>If this is false, the Ribbon is missing.</remarks>
|
||||
public bool Invalid { get; }
|
||||
|
||||
public RibbonResult(string prop, bool invalid = true)
|
||||
{
|
||||
Name = RibbonStrings.GetName(prop) ?? prop;
|
||||
Invalid = invalid;
|
||||
}
|
||||
|
||||
public void Combine(RibbonResult other)
|
||||
{
|
||||
Name += " / " + other.Name;
|
||||
}
|
||||
}
|
||||
}
|
299
PKHeX.Core/Legality/Ribbons/RibbonVerifier.cs
Normal file
299
PKHeX.Core/Legality/Ribbons/RibbonVerifier.cs
Normal file
|
@ -0,0 +1,299 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
internal static class RibbonVerifier
|
||||
{
|
||||
internal static List<string> GetIncorrectRibbons(PKM pkm, object encounterContent, int gen)
|
||||
{
|
||||
List<string> missingRibbons = new List<string>();
|
||||
List<string> invalidRibbons = new List<string>();
|
||||
IEnumerable<RibbonResult> ribs = GetRibbonResults(pkm, encounterContent, gen);
|
||||
foreach (var bad in ribs)
|
||||
(bad.Invalid ? invalidRibbons : missingRibbons).Add(bad.Name);
|
||||
|
||||
var result = new List<string>();
|
||||
if (missingRibbons.Count > 0)
|
||||
result.Add(string.Format(V600, string.Join(", ", missingRibbons.Select(z => z.Replace("Ribbon", "")))));
|
||||
if (invalidRibbons.Count > 0)
|
||||
result.Add(string.Format(V601, string.Join(", ", invalidRibbons.Select(z => z.Replace("Ribbon", "")))));
|
||||
return result;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetRibbonResults(PKM pkm, object encounterContent, int gen)
|
||||
{
|
||||
return GetInvalidRibbons(pkm, gen)
|
||||
.Concat(GetInvalidRibbonsEvent1(pkm, encounterContent))
|
||||
.Concat(GetInvalidRibbonsEvent2(pkm, encounterContent));
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, int gen)
|
||||
{
|
||||
bool artist = false;
|
||||
if (pkm is IRibbonSetOnly3 o3)
|
||||
{
|
||||
artist = o3.RibbonCounts().Any(z => z == 4);
|
||||
}
|
||||
if (pkm is IRibbonSetUnique3 u3)
|
||||
{
|
||||
if (gen != 3 || !IsAllowedBattleFrontier(pkm.Species))
|
||||
{
|
||||
if (u3.RibbonWinning)
|
||||
yield return new RibbonResult(nameof(u3.RibbonWinning));
|
||||
if (u3.RibbonVictory)
|
||||
yield return new RibbonResult(nameof(u3.RibbonVictory));
|
||||
}
|
||||
}
|
||||
if (pkm is IRibbonSetUnique4 u4)
|
||||
{
|
||||
if (!IsAllowedBattleFrontier(pkm.Species, pkm.AltForm, 4))
|
||||
foreach (var z in GetInvalidRibbonsNone(u4.RibbonBitsAbility(), u4.RibbonNamesAbility()))
|
||||
yield return z;
|
||||
|
||||
var c3 = u4.RibbonBitsContest3(); var c3n = u4.RibbonNamesContest3();
|
||||
var c4 = u4.RibbonBitsContest4(); var c4n = u4.RibbonNamesContest4();
|
||||
var iter3 = gen == 3 ? getMissingContestRibbons(c3, c3n) : GetInvalidRibbonsNone(c3, c3n);
|
||||
var iter4 = (gen == 3 || gen == 4) && IsAllowedInContest4(pkm.Species) ? getMissingContestRibbons(c4, c4n) : GetInvalidRibbonsNone(c4, c4n);
|
||||
foreach (var z in iter3.Concat(iter4))
|
||||
yield return z;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
artist |= c3[3 | i << 2]; // any master rank ribbon
|
||||
|
||||
IEnumerable<RibbonResult> getMissingContestRibbons(IReadOnlyList<bool> bits, IReadOnlyList<string> names)
|
||||
{
|
||||
for (int i = 0; i < bits.Count; i += 4)
|
||||
{
|
||||
bool required = false;
|
||||
for (int j = i + 3; j >= i; j--)
|
||||
if (bits[j])
|
||||
required = true;
|
||||
else if (required)
|
||||
yield return new RibbonResult(names[j], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pkm is IRibbonSetCommon4 s4)
|
||||
{
|
||||
bool inhabited4 = 3 <= gen && gen <= 4;
|
||||
IEnumerable<RibbonResult> iterate = GetInvalidRibbons4Any(pkm, s4, gen);
|
||||
if (!inhabited4)
|
||||
iterate = iterate.Concat(GetInvalidRibbonsNone(s4.RibbonBitsOnly(), s4.RibbonNamesOnly()));
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
}
|
||||
if (pkm is IRibbonSetCommon6 s6)
|
||||
{
|
||||
artist = s6.RibbonCountMemoryContest > 4;
|
||||
bool inhabited6 = 3 <= gen && gen <= 6;
|
||||
|
||||
var iterate = inhabited6
|
||||
? GetInvalidRibbons6Any(pkm, s6, gen)
|
||||
: GetInvalidRibbonsNone(s6.RibbonBits(), s6.RibbonNamesBool());
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
|
||||
if (!inhabited6)
|
||||
{
|
||||
if (s6.RibbonCountMemoryContest > 0)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryContest));
|
||||
if (s6.RibbonCountMemoryBattle > 0)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryBattle));
|
||||
}
|
||||
|
||||
if (s6.RibbonBestFriends && pkm.OT_Affection < 255 && pkm.IsUntraded) // can't lower affection
|
||||
yield return new RibbonResult(nameof(s6.RibbonBestFriends));
|
||||
}
|
||||
if (pkm is IRibbonSetCommon7 s7)
|
||||
{
|
||||
bool inhabited7 = 3 <= gen && gen <= 7 || gen == 1;
|
||||
var iterate = inhabited7 ? GetInvalidRibbons7Any(pkm, s7) : GetInvalidRibbonsNone(s7.RibbonBits(), s7.RibbonNames());
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
}
|
||||
if (pkm is IRibbonSetCommon3 s3)
|
||||
{
|
||||
if (s3.RibbonChampionG3Hoenn && gen != 3)
|
||||
yield return new RibbonResult(nameof(s3.RibbonChampionG3Hoenn)); // RSE HoF
|
||||
if (s3.RibbonArtist && (gen != 3 || !artist))
|
||||
yield return new RibbonResult(nameof(s3.RibbonArtist)); // RSE Master Rank Portrait
|
||||
if (s3.RibbonEffort && gen == 5 && pkm.Format == 5) // unobtainable in Gen 5
|
||||
yield return new RibbonResult(nameof(s3.RibbonEffort));
|
||||
}
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons4Any(PKM pkm, IRibbonSetCommon4 s4, int gen)
|
||||
{
|
||||
if (s4.RibbonRecord)
|
||||
yield return new RibbonResult(nameof(s4.RibbonRecord)); // Unobtainable
|
||||
if (s4.RibbonFootprint && (pkm.Format < 6 && gen == 5 || gen >= 6 && pkm.CurrentLevel - pkm.Met_Level < 30))
|
||||
yield return new RibbonResult(nameof(s4.RibbonFootprint));
|
||||
|
||||
bool gen34 = gen == 3 || gen == 4;
|
||||
bool not6 = pkm.Format < 6 || gen > 6 || gen < 3;
|
||||
bool noDaily = !gen34 && not6;
|
||||
bool noCosmetic = !gen34 && (not6 || pkm.XY && pkm.IsUntraded);
|
||||
|
||||
if (noDaily)
|
||||
foreach (var z in GetInvalidRibbonsNone(s4.RibbonBitsDaily(), s4.RibbonNamesDaily()))
|
||||
yield return z;
|
||||
if (noCosmetic)
|
||||
foreach (var z in GetInvalidRibbonsNone(s4.RibbonBitsCosmetic(), s4.RibbonNamesCosmetic()))
|
||||
yield return z;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons6Any(PKM pkm, IRibbonSetCommon6 s6, int gen)
|
||||
{
|
||||
foreach (var p in GetInvalidRibbons6Memory(pkm, s6, gen))
|
||||
yield return p;
|
||||
|
||||
bool untraded = pkm.IsUntraded;
|
||||
var iter = untraded ? GetInvalidRibbons6Untraded(pkm, s6) : GetInvalidRibbons6Traded(pkm, s6);
|
||||
foreach (var p in iter)
|
||||
yield return p;
|
||||
|
||||
bool allContest = s6.RibbonBitsContest().All(z => z);
|
||||
if (allContest ^ s6.RibbonContestStar && !(untraded && pkm.XY)) // if not already checked
|
||||
yield return new RibbonResult(nameof(s6.RibbonContestStar), s6.RibbonContestStar);
|
||||
|
||||
const int mem_Chatelaine = 30;
|
||||
bool hasChampMemory = pkm.HT_Memory == mem_Chatelaine || pkm.OT_Memory == mem_Chatelaine;
|
||||
if (!hasChampMemory || s6.RibbonBattlerSkillful || s6.RibbonBattlerExpert)
|
||||
yield break;
|
||||
|
||||
var result = new RibbonResult(nameof(s6.RibbonBattlerSkillful), false);
|
||||
result.Combine(new RibbonResult(nameof(s6.RibbonBattlerExpert)));
|
||||
yield return result;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons6Memory(PKM pkm, IRibbonSetCommon6 s6, int gen)
|
||||
{
|
||||
int contest = 0;
|
||||
int battle = 0;
|
||||
switch (gen)
|
||||
{
|
||||
case 3:
|
||||
contest = IsAllowedInContest4(pkm.Species) ? 40 : 20;
|
||||
battle = IsAllowedBattleFrontier(pkm.Species) ? 8 : 0;
|
||||
break;
|
||||
case 4:
|
||||
contest = IsAllowedInContest4(pkm.Species) ? 20 : 0;
|
||||
battle = IsAllowedBattleFrontier(pkm.Species) ? 6 : 0;
|
||||
break;
|
||||
}
|
||||
if (s6.RibbonCountMemoryContest > contest)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryContest));
|
||||
if (s6.RibbonCountMemoryBattle > battle)
|
||||
yield return new RibbonResult(nameof(s6.RibbonCountMemoryBattle));
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons6Untraded(PKM pkm, IRibbonSetCommon6 s6)
|
||||
{
|
||||
if (pkm.XY)
|
||||
{
|
||||
if (s6.RibbonChampionG6Hoenn)
|
||||
yield return new RibbonResult(nameof(s6.RibbonChampionG6Hoenn));
|
||||
|
||||
if (s6.RibbonContestStar)
|
||||
yield return new RibbonResult(nameof(s6.RibbonContestStar));
|
||||
if (s6.RibbonMasterCoolness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterCoolness));
|
||||
if (s6.RibbonMasterBeauty)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterBeauty));
|
||||
if (s6.RibbonMasterCuteness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterCuteness));
|
||||
if (s6.RibbonMasterCleverness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterCleverness));
|
||||
if (s6.RibbonMasterToughness)
|
||||
yield return new RibbonResult(nameof(s6.RibbonMasterToughness));
|
||||
}
|
||||
else if (pkm.AO)
|
||||
{
|
||||
if (s6.RibbonChampionKalos)
|
||||
yield return new RibbonResult(nameof(s6.RibbonChampionKalos));
|
||||
}
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons6Traded(PKM pkm, IRibbonSetCommon6 s6)
|
||||
{
|
||||
if (s6.RibbonTraining)
|
||||
{
|
||||
const int req = 12; // only first 12
|
||||
int count = pkm.SuperTrainingMedalCount(req);
|
||||
if (count < req)
|
||||
yield return new RibbonResult(nameof(s6.RibbonTraining));
|
||||
}
|
||||
|
||||
const int mem_Champion = 27;
|
||||
bool hasChampMemory = pkm.HT_Memory == mem_Champion || pkm.OT_Memory == mem_Champion;
|
||||
if (!hasChampMemory || s6.RibbonChampionKalos || s6.RibbonChampionG6Hoenn)
|
||||
yield break;
|
||||
|
||||
var result = new RibbonResult(nameof(s6.RibbonChampionKalos), false);
|
||||
result.Combine(new RibbonResult(nameof(s6.RibbonChampionG6Hoenn)));
|
||||
yield return result;
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons7Any(PKM pkm, IRibbonSetCommon7 s7)
|
||||
{
|
||||
if (!IsAllowedBattleFrontier(pkm.Species))
|
||||
{
|
||||
if (s7.RibbonBattleRoyale)
|
||||
yield return new RibbonResult(nameof(s7.RibbonBattleRoyale));
|
||||
if (s7.RibbonBattleTreeGreat)
|
||||
yield return new RibbonResult(nameof(s7.RibbonBattleTreeGreat));
|
||||
if (s7.RibbonBattleTreeMaster)
|
||||
yield return new RibbonResult(nameof(s7.RibbonBattleTreeMaster));
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbonsEvent1(PKM pkm, object encounterContent)
|
||||
{
|
||||
if (!(pkm is IRibbonSetEvent3 set1))
|
||||
yield break;
|
||||
var names = set1.RibbonNames();
|
||||
var sb = set1.RibbonBits();
|
||||
var eb = (encounterContent as IRibbonSetEvent3).RibbonBits();
|
||||
|
||||
if (pkm.Gen3)
|
||||
{
|
||||
eb[0] = sb[0]; // permit Earth Ribbon
|
||||
if (pkm.Version == 15 && encounterContent is EncounterStaticShadow s)
|
||||
{
|
||||
// only require national ribbon if no longer on origin game
|
||||
bool xd = !Encounters3.Encounter_Colo.Contains(s);
|
||||
eb[1] = !(xd && pkm is XK3 x && !x.RibbonNational || !xd && pkm is CK3 c && !c.RibbonNational);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sb.Length; i++)
|
||||
if (sb[i] != eb[i])
|
||||
yield return new RibbonResult(names[i], !eb[i]); // only flag if invalid
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbonsEvent2(PKM pkm, object encounterContent)
|
||||
{
|
||||
if (!(pkm is IRibbonSetEvent4 set2))
|
||||
yield break;
|
||||
var names = set2.RibbonNames();
|
||||
var sb = set2.RibbonBits();
|
||||
var eb = (encounterContent as IRibbonSetEvent4).RibbonBits();
|
||||
|
||||
if (encounterContent is EncounterStatic s && s.RibbonWishing)
|
||||
eb[1] = true; // require Wishing Ribbon
|
||||
|
||||
for (int i = 0; i < sb.Length; i++)
|
||||
if (sb[i] != eb[i])
|
||||
yield return new RibbonResult(names[i], !eb[i]); // only flag if invalid
|
||||
}
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbonsNone(IReadOnlyList<bool> bits, IReadOnlyList<string> names)
|
||||
{
|
||||
for (int i = 0; i < bits.Count; i++)
|
||||
if (bits[i])
|
||||
yield return new RibbonResult(names[i]);
|
||||
}
|
||||
|
||||
private static bool IsAllowedInContest4(int species) => species != 201 && species != 132; // Disallow Unown and Ditto
|
||||
private static bool IsAllowedBattleFrontier(int species, int form = 0, int gen = 0)
|
||||
{
|
||||
if (gen == 4 && species == 172 && form == 1) // spiky
|
||||
return false;
|
||||
|
||||
return !Legal.BattleFrontierBanlist.Contains(species);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue