Initial Gen1 legality checks (WIP)

Have only tested pk1 format so far; there's still a few cases that are
unhandled (move discrepancies between games, trade gifts, event mew,
tmhm).

pls no report bugs unless you are very detailed (include WHY it is
wrong).
This commit is contained in:
Kurt 2017-02-12 17:00:03 -08:00
parent 22e49928c5
commit 578f05cd2e
9 changed files with 369 additions and 202 deletions

View file

@ -2837,7 +2837,9 @@ namespace PKHeX.WinForms
LegalityAnalysis la = new LegalityAnalysis(pk);
if (!la.Parsed)
{
WinFormsUtil.Alert($"Checking legality of PK{pk.Format} files that originated from Gen{pk.GenNumber} is not supported.");
WinFormsUtil.Alert(pk.Format < 3
? $"Checking legality of PK{pk.Format} files is not supported."
: $"Checking legality of PK{pk.Format} files that originated from Gen{pk.GenNumber} is not supported.");
return;
}
if (tabs)
@ -2846,48 +2848,43 @@ namespace PKHeX.WinForms
}
private void updateLegality(LegalityAnalysis la = null, bool skipMoveRepop = false)
{
if (pkm.GenNumber >= 6)
if (!fieldsLoaded)
return;
Legality = la ?? new LegalityAnalysis(pkm);
if (!Legality.Parsed || HaX)
{
if (!fieldsLoaded)
return;
Legality = la ?? new LegalityAnalysis(pkm);
if (!Legality.Parsed || HaX)
{
PB_Legal.Visible = false;
return;
}
PB_Legal.Visible = true;
PB_Legal.Image = Legality.Valid ? Resources.valid : Resources.warn;
// Refresh Move Legality
for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vMoves[i].Valid && !HaX;
for (int i = 0; i < 4; i++)
relearnPB[i].Visible = !Legality.vRelearn[i].Valid && !HaX;
if (skipMoveRepop)
return;
// Resort moves
bool tmp = fieldsLoaded;
fieldsLoaded = false;
var cb = new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
var moves = Legality.AllSuggestedMovesAndRelearn;
var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList();
foreach (ComboBox c in cb)
{
var index = WinFormsUtil.getIndex(c);
c.DataSource = new BindingSource(moveList, null);
c.SelectedValue = index;
}
fieldsLoaded |= tmp;
}
else
{
PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible =
PB_Legal.Visible =
PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible =
PB_WarnRelearn1.Visible = PB_WarnRelearn2.Visible = PB_WarnRelearn3.Visible = PB_WarnRelearn4.Visible = false;
return;
}
PB_Legal.Visible = true;
PB_Legal.Image = Legality.Valid ? Resources.valid : Resources.warn;
// Refresh Move Legality
for (int i = 0; i < 4; i++)
movePB[i].Visible = !Legality.vMoves[i].Valid && !HaX;
for (int i = 0; i < 4; i++)
relearnPB[i].Visible = !Legality.vRelearn[i].Valid && !HaX && pkm.Format >= 6;
if (skipMoveRepop)
return;
// Resort moves
bool tmp = fieldsLoaded;
fieldsLoaded = false;
var cb = new[] {CB_Move1, CB_Move2, CB_Move3, CB_Move4};
var moves = Legality.AllSuggestedMovesAndRelearn;
var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList();
foreach (ComboBox c in cb)
{
var index = WinFormsUtil.getIndex(c);
c.DataSource = new BindingSource(moveList, null);
c.SelectedValue = index;
}
fieldsLoaded |= tmp;
}
private void updateGender()

View file

@ -39,11 +39,14 @@ namespace PKHeX.Core
try
{
switch (pk.Format) // prior to storing GameVersion
{
case 1: parsePK1(pk); break;
}
switch (pk.GenNumber)
{
case 6: parsePK6(pk); break;
case 7: parsePK7(pk); break;
default: return;
}
Valid = Parsed = Parse.Any();
@ -59,6 +62,8 @@ namespace PKHeX.Core
if (pkm.FatefulEncounter && vRelearn.Any(chk => !chk.Valid) && EncounterMatch == null)
AddLine(Severity.Indeterminate, "Fateful Encounter with no matching Encounter. Has the Mystery Gift data been contributed?", CheckIdentifier.Fateful);
}
else
return;
}
catch { Valid = false; }
AllSuggestedMoves = !pkm.IsOriginValid() ? new int[4] : getSuggestedMoves(true, true, true);
@ -74,6 +79,19 @@ namespace PKHeX.Core
{
Parse.Add(chk);
}
private void parsePK1(PKM pk)
{
pkm = pk;
if (!pkm.IsOriginValid())
{ AddLine(Severity.Invalid, "Species does not exist in origin game.", CheckIdentifier.None); return; }
updateEncounterChain();
updateMoveLegality();
verifyNickname();
verifyDVs();
verifyG1OT();
}
private void parsePK6(PKM pk)
{
pkm = pk;
@ -116,6 +134,7 @@ namespace PKHeX.Core
EncounterMatch = EventGiftMatch.First(); // temporarily set one so that Encounter can be verified
Encounter = verifyEncounter();
Parse.Add(Encounter);
EvoChain = Legal.getEvolutionChain(pkm, EncounterMatch);
}
private void updateChecks()
@ -160,6 +179,8 @@ namespace PKHeX.Core
for (int i = 0; i < 4; i++)
if (!vMoves[i].Valid)
r += $"{vMoves[i].Judgement} Move {i + 1}: {vMoves[i].Comment}" + Environment.NewLine;
if (pkm.Format >= 6)
for (int i = 0; i < 4; i++)
if (!vRelearn[i].Valid)
r += $"{vRelearn[i].Judgement} Relearn Move {i + 1}: {vRelearn[i].Comment}" + Environment.NewLine;
@ -186,7 +207,9 @@ namespace PKHeX.Core
for (int i = 0; i < 4; i++)
if (vMoves[i].Valid)
r += $"{vMoves[i].Judgement} Move {i + 1}: {vMoves[i].Comment}" + Environment.NewLine;
for (int i = 0; i < 4; i++)
if (pkm.Format >= 6)
for (int i = 0; i < 4; i++)
if (vRelearn[i].Valid)
r += $"{vRelearn[i].Judgement} Relearn Move {i + 1}: {vRelearn[i].Comment}" + Environment.NewLine;
@ -222,8 +245,10 @@ namespace PKHeX.Core
}
public int[] getSuggestedMoves(bool tm, bool tutor, bool reminder)
{
if (pkm == null || pkm.GenNumber < 6 || !pkm.IsOriginValid())
if (pkm == null || !pkm.IsOriginValid())
return null;
if (pkm.GenNumber < 6 && pkm.Format != 1)
return new int[4];
return Legal.getValidMoves(pkm, EvoChain, Tutor: tutor, Machine: tm, MoveReminder: reminder).Skip(1).ToArray(); // skip move 0
}

View file

@ -250,10 +250,13 @@ namespace PKHeX.Core
return;
}
AddLine(Severity.Valid, "Nickname does not match another species name.", CheckIdentifier.EVs);
return;
}
// else
else if (pkm.Format < 3)
{
// pk1/pk2 IsNicknamed getter checks for match, logic should only reach here if matches.
AddLine(Severity.Valid, "Nickname matches species name.", CheckIdentifier.EVs);
}
else
{
// Can't have another language name if it hasn't evolved or wasn't a language-traded egg.
bool match = (pkm.WasTradedEgg || Legal.getHasEvolved(pkm)) && PKX.SpeciesLang.Any(lang => lang[pkm.Species] == nickname)
@ -263,8 +266,6 @@ namespace PKHeX.Core
AddLine(Severity.Invalid, "Nickname does not match species name.", CheckIdentifier.EVs);
else
AddLine(Severity.Valid, "Nickname matches species name.", CheckIdentifier.EVs);
// return;
}
}
private void verifyEVs()
@ -328,6 +329,10 @@ namespace PKHeX.Core
else if (pkm.IVs[0] < 30 && pkm.IVs.All(iv => pkm.IVs[0] == iv))
AddLine(Severity.Fishy, "All IVs are equal.", CheckIdentifier.IVs);
}
private void verifyDVs()
{
// todo
}
private void verifyOT()
{
if (EncounterType == typeof(EncounterTrade))
@ -348,19 +353,21 @@ namespace PKHeX.Core
AddLine(Severity.Fishy, "SID is zero.", CheckIdentifier.Trainer);
if (pkm.VC)
{
string tr = pkm.OT_Name;
string pk = pkm.Nickname;
var langset = PKX.SpeciesLang.FirstOrDefault(s => s.Contains(pk)) ?? PKX.SpeciesLang[2];
int lang = Array.IndexOf(PKX.SpeciesLang, langset);
verifyG1OT();
}
private void verifyG1OT()
{
string tr = pkm.OT_Name;
string pk = pkm.Nickname;
var langset = PKX.SpeciesLang.FirstOrDefault(s => s.Contains(pk)) ?? PKX.SpeciesLang[2];
int lang = Array.IndexOf(PKX.SpeciesLang, langset);
if (tr.Length > (lang == 2 ? 7 : 5))
AddLine(Severity.Invalid, "OT Name too long.", CheckIdentifier.Trainer);
if (pkm.Species == 151)
{
if (tr != "GF" && tr != "ゲーフリ") // if there are more events with special OTs, may be worth refactoring
AddLine(Severity.Invalid, "Incorrect event OT Name.", CheckIdentifier.Trainer);
}
if (tr.Length > (lang == 2 ? 7 : 5))
AddLine(Severity.Invalid, "OT Name too long.", CheckIdentifier.Trainer);
if (pkm.Species == 151)
{
if (tr != "GF" && tr != "ゲーフリ") // if there are more events with special OTs, may be worth refactoring
AddLine(Severity.Invalid, "Incorrect event OT Name.", CheckIdentifier.Trainer);
}
}
@ -386,151 +393,183 @@ namespace PKHeX.Core
}
}
}
private CheckResult verifyEncounterLink()
{
// Should NOT be Fateful, and should be in Database
EncounterLink enc = EncounterMatch as EncounterLink;
if (enc == null)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: unable to find matching gift.", CheckIdentifier.Encounter);
if (pkm.XY && !enc.XY)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in XY.", CheckIdentifier.Encounter);
if (pkm.AO && !enc.ORAS)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in ORAS.", CheckIdentifier.Encounter);
if (pkm.SM && !enc.SM)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in SM.", CheckIdentifier.Encounter);
if (enc.Shiny != null && (bool)enc.Shiny ^ pkm.IsShiny)
return new CheckResult(Severity.Invalid, "Shiny Link gift mismatch.", CheckIdentifier.Encounter);
return pkm.FatefulEncounter
? new CheckResult(Severity.Invalid, "Invalid Link Gift: should not be Fateful Encounter.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid Link gift.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterEvent()
{
MysteryGift MatchedGift = EncounterMatch as MysteryGift;
if (MatchedGift != null)
return new CheckResult(Severity.Valid, $"Matches #{MatchedGift.CardID:0000} ({MatchedGift.CardTitle})", CheckIdentifier.Encounter);
return null;
}
private CheckResult verifyEncounterEgg()
{
// Check Hatch Locations
if (pkm.Met_Level != 1)
return new CheckResult(Severity.Invalid, "Invalid met level, expected 1.", CheckIdentifier.Encounter);
// Check species
if (Legal.NoHatchFromEgg.Contains(pkm.Species))
return new CheckResult(Severity.Invalid, "Species cannot be hatched from an egg.", CheckIdentifier.Encounter);
if (pkm.IsEgg)
{
if (pkm.Egg_Location == 30002)
return new CheckResult(Severity.Invalid, "Egg location shouldn't be 'traded' for an un-hatched egg.", CheckIdentifier.Encounter);
if (pkm.Met_Location == 30002)
return new CheckResult(Severity.Valid, "Valid traded un-hatched egg.", CheckIdentifier.Encounter);
return pkm.Met_Location == 0
? new CheckResult(Severity.Valid, "Valid un-hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid location for un-hatched egg (expected no met location).", CheckIdentifier.Encounter);
}
if (pkm.XY)
{
if (pkm.Egg_Location == 318)
return new CheckResult(Severity.Invalid, "Invalid X/Y egg location.", CheckIdentifier.Encounter);
return Legal.ValidMet_XY.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid X/Y hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid X/Y location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.AO)
{
return Legal.ValidMet_AO.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid OR/AS hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid OR/AS location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.SM)
{
return Legal.ValidMet_SM.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid S/M hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid S/M location for hatched egg.", CheckIdentifier.Encounter);
}
return new CheckResult(Severity.Invalid, "Invalid location for hatched egg.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterSafari()
{
if (pkm.Species == 670 || pkm.Species == 671) // Floette
if (!new[] {0, 1, 3}.Contains(pkm.AltForm)) // 0/1/3 - RBY
return new CheckResult(Severity.Invalid, "Friend Safari: Not valid color.", CheckIdentifier.Encounter);
else if (pkm.Species == 710 || pkm.Species == 711) // Pumpkaboo
if (pkm.AltForm != 1) // Average
return new CheckResult(Severity.Invalid, "Friend Safari: Not average sized.", CheckIdentifier.Encounter);
else if (pkm.Species == 586) // Sawsbuck
if (pkm.AltForm != 0)
return new CheckResult(Severity.Invalid, "Friend Safari: Not Spring form.", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid Friend Safari encounter.", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterWild()
{
EncounterSlot[] enc = (EncounterSlot[])EncounterMatch;
if (enc.Any(slot => slot.Normal))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location.", CheckIdentifier.Encounter);
// Decreased Level Encounters
if (enc.Any(slot => slot.WhiteFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (White Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (White Flute).", CheckIdentifier.Encounter);
// Increased Level Encounters
if (enc.Any(slot => slot.BlackFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute).", CheckIdentifier.Encounter);
if (enc.Any(slot => slot.Pressure))
return new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid encounter at location (DexNav).", CheckIdentifier.Encounter);
}
private CheckResult verifyEncounterStatic()
{
// Re-parse relearn moves
var s = (EncounterStatic)EncounterMatch;
if (s.EggLocation != 60002 || vRelearn.Any(rl => !rl.Valid))
{
for (int i = 0; i < 4; i++)
vRelearn[i] = pkm.RelearnMoves[i] != s.Relearn[i]
? new CheckResult(Severity.Invalid, "Static encounter relearn move mismatch.", CheckIdentifier.RelearnMove)
: new CheckResult(CheckIdentifier.RelearnMove);
return new CheckResult(Severity.Valid, "Valid gift/static encounter.", CheckIdentifier.Encounter);
}
return null;
}
private CheckResult verifyEncounter()
{
if (pkm.GenNumber < 6)
return new CheckResult(Severity.NotImplemented, "Not Implemented.", CheckIdentifier.Encounter);
if (pkm.VC)
if (pkm.VC || pkm.Format < 3)
{
int baseSpecies = Legal.getBaseSpecies(pkm);
if ((pkm.VC1 && baseSpecies > Legal.MaxSpeciesID_1) ||
(pkm.VC2 && baseSpecies > Legal.MaxSpeciesID_2))
bool g1 = pkm.VC1 || pkm.Format == 1;
if ((g1 && baseSpecies > Legal.MaxSpeciesID_1) || (baseSpecies > Legal.MaxSpeciesID_2))
return new CheckResult(Severity.Invalid, "VC: Unobtainable species.", CheckIdentifier.Encounter);
return verifyVCEncounter(baseSpecies);
if (pkm.Format > 2) // transported to 7+
Parse.Add(verifyVCEncounter(baseSpecies));
}
if (pkm.WasLink)
else if (pkm.GenNumber == 4)
{
// Should NOT be Fateful, and should be in Database
EncounterLink enc = EncounterMatch as EncounterLink;
if (enc == null)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: unable to find matching gift.", CheckIdentifier.Encounter);
if (pkm.XY && !enc.XY)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in XY.", CheckIdentifier.Encounter);
if (pkm.AO && !enc.ORAS)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in ORAS.", CheckIdentifier.Encounter);
if (pkm.SM && !enc.SM)
return new CheckResult(Severity.Invalid, "Invalid Link Gift: can't obtain in SM.", CheckIdentifier.Encounter);
if (enc.Shiny != null && (bool)enc.Shiny ^ pkm.IsShiny)
return new CheckResult(Severity.Invalid, "Shiny Link gift mismatch.", CheckIdentifier.Encounter);
return pkm.FatefulEncounter
? new CheckResult(Severity.Invalid, "Invalid Link Gift: should not be Fateful Encounter.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid Link gift.", CheckIdentifier.Encounter);
}
else
{
if (pkm.WasLink)
return verifyEncounterLink();
}
if (pkm.WasEvent || pkm.WasEventEgg)
{
MysteryGift MatchedGift = EncounterMatch as MysteryGift;
if (MatchedGift != null)
return new CheckResult(Severity.Valid, $"Matches #{MatchedGift.CardID:0000} ({MatchedGift.CardTitle})", CheckIdentifier.Encounter);
var result = verifyEncounterEvent();
if (result != null)
return result;
}
EncounterMatch = Legal.getValidStaticEncounter(pkm);
if (EncounterMatch != null)
{
// Re-parse relearn moves
var s = (EncounterStatic)EncounterMatch;
if (s.EggLocation != 60002 || vRelearn.Any(rl => !rl.Valid))
{
for (int i = 0; i < 4; i++)
vRelearn[i] = pkm.RelearnMoves[i] != s.Relearn[i]
? new CheckResult(Severity.Invalid, "Static encounter relearn move mismatch.", CheckIdentifier.RelearnMove)
: new CheckResult(CheckIdentifier.RelearnMove);
return new CheckResult(Severity.Valid, "Valid gift/static encounter.", CheckIdentifier.Encounter);
}
var result = verifyEncounterStatic();
if (result != null)
return result;
}
EncounterMatch = null; // Reset object
// Reset Encounter Object, test for remaining encounters
EncounterMatch = null;
if (pkm.WasEgg)
{
// Check Hatch Locations
if (pkm.Met_Level != 1)
return new CheckResult(Severity.Invalid, "Invalid met level, expected 1.", CheckIdentifier.Encounter);
// Check species
if (Legal.NoHatchFromEgg.Contains(pkm.Species))
return new CheckResult(Severity.Invalid, "Species cannot be hatched from an egg.", CheckIdentifier.Encounter);
if (pkm.IsEgg)
{
if (pkm.Egg_Location == 30002)
return new CheckResult(Severity.Invalid, "Egg location shouldn't be 'traded' for an un-hatched egg.", CheckIdentifier.Encounter);
if (pkm.Met_Location == 30002)
return new CheckResult(Severity.Valid, "Valid traded un-hatched egg.", CheckIdentifier.Encounter);
return pkm.Met_Location == 0
? new CheckResult(Severity.Valid, "Valid un-hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid location for un-hatched egg (expected no met location).", CheckIdentifier.Encounter);
}
if (pkm.XY)
{
if (pkm.Egg_Location == 318)
return new CheckResult(Severity.Invalid, "Invalid X/Y egg location.", CheckIdentifier.Encounter);
return Legal.ValidMet_XY.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid X/Y hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid X/Y location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.AO)
{
return Legal.ValidMet_AO.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid OR/AS hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid OR/AS location for hatched egg.", CheckIdentifier.Encounter);
}
if (pkm.SM)
{
return Legal.ValidMet_SM.Contains(pkm.Met_Location)
? new CheckResult(Severity.Valid, "Valid S/M hatched egg.", CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, "Invalid S/M location for hatched egg.", CheckIdentifier.Encounter);
}
return new CheckResult(Severity.Invalid, "Invalid location for hatched egg.", CheckIdentifier.Encounter);
}
return verifyEncounterEgg();
EncounterMatch = Legal.getValidFriendSafari(pkm);
if (EncounterMatch != null)
{
if (pkm.Species == 670 || pkm.Species == 671) // Floette
if (!new[] {0, 1, 3}.Contains(pkm.AltForm)) // 0/1/3 - RBY
return new CheckResult(Severity.Invalid, "Friend Safari: Not valid color.", CheckIdentifier.Encounter);
else if (pkm.Species == 710 || pkm.Species == 711) // Pumpkaboo
if (pkm.AltForm != 1) // Average
return new CheckResult(Severity.Invalid, "Friend Safari: Not average sized.", CheckIdentifier.Encounter);
else if (pkm.Species == 586) // Sawsbuck
if (pkm.AltForm != 0)
return new CheckResult(Severity.Invalid, "Friend Safari: Not Spring form.", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid Friend Safari encounter.", CheckIdentifier.Encounter);
}
return verifyEncounterSafari();
EncounterMatch = Legal.getValidWildEncounters(pkm);
if (EncounterMatch != null)
{
EncounterSlot[] enc = (EncounterSlot[])EncounterMatch;
return verifyEncounterWild();
if (enc.Any(slot => slot.Normal))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location.", CheckIdentifier.Encounter);
// Decreased Level Encounters
if (enc.Any(slot => slot.WhiteFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (White Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (White Flute).", CheckIdentifier.Encounter);
// Increased Level Encounters
if (enc.Any(slot => slot.BlackFlute))
return enc.All(slot => slot.Pressure)
? new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute & Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter)
: new CheckResult(Severity.Valid, "Valid encounter at location (Black Flute).", CheckIdentifier.Encounter);
if (enc.Any(slot => slot.Pressure))
return new CheckResult(Severity.Valid, "Valid encounter at location (Pressure/Hustle/Vital Spirit).", CheckIdentifier.Encounter);
return new CheckResult(Severity.Valid, "Valid encounter at location (DexNav).", CheckIdentifier.Encounter);
}
EncounterMatch = Legal.getValidIngameTrade(pkm);
if (EncounterMatch != null)
return new CheckResult(Severity.Valid, "Valid ingame trade.", CheckIdentifier.Encounter);
@ -1777,9 +1816,7 @@ namespace PKHeX.Core
CheckResult[] res = new CheckResult[4];
for (int i = 0; i < 4; i++)
res[i] = new CheckResult(CheckIdentifier.Move);
if (pkm.GenNumber < 6 || pkm.VC1)
return res;
var validMoves = Legal.getValidMoves(pkm, EvoChain).ToArray();
if (pkm.Species == 235) // Smeargle
{

View file

@ -12,6 +12,7 @@ namespace PKHeX.Core
// Gen 1
private static readonly Learnset[] LevelUpRB = Learnset1.getArray(Resources.lvlmove_rby);
private static readonly Learnset[] LevelUpY = Learnset1.getArray(Resources.lvlmove_rby);
private static readonly EvolutionTree Evolves1;
private static readonly EncounterArea[] SlotsRBY;
private static readonly EncounterStatic[] StaticRBY;
@ -282,12 +283,15 @@ namespace PKHeX.Core
continue;
if (e.EggLocation != pkm.Egg_Location)
continue;
if (pkm.HasOriginalMetLocation && e.Location != 0 && e.Location != pkm.Met_Location)
continue;
if (pkm.HasOriginalMetLocation)
{
if (e.Location != 0 && e.Location != pkm.Met_Location)
continue;
if (e.Level != pkm.Met_Level)
continue;
}
if (e.Gender != -1 && e.Gender != pkm.Gender)
continue;
if (e.Level != pkm.Met_Level)
continue;
if (e.Form != pkm.AltForm && !FormChange.Contains(pkm.Species) && !e.SkipFormCheck)
continue;
@ -783,7 +787,7 @@ namespace PKHeX.Core
}
if (t is int)
return "Unknown";
return t.GetType().Name;
return t?.GetType().Name ?? "Unknown";
}
private static IEnumerable<EncounterArea> getDexNavAreas(PKM pkm)
{
@ -802,7 +806,8 @@ namespace PKHeX.Core
List<int> moves = new List<int>();
if (pkm.InhabitedGeneration(1))
{
moves.AddRange(LevelUpRB[species].getMoves(lvl));
moves.AddRange(((PersonalInfoG1)PersonalTable.RBY[species]).Moves);
moves.AddRange(LevelUpY[species].getMoves(lvl));
}
if (pkm.InhabitedGeneration(6))
{
@ -822,6 +827,7 @@ namespace PKHeX.Core
{
switch ((GameVersion)pkm.Version)
{
case GameVersion.RBY:
case GameVersion.RD: case GameVersion.BU:
case GameVersion.GN: case GameVersion.YW:
return getSlots(pkm, SlotsRBY, lvl);
@ -846,6 +852,7 @@ namespace PKHeX.Core
{
switch ((GameVersion)pkm.Version)
{
case GameVersion.RBY:
case GameVersion.RD: case GameVersion.BU:
case GameVersion.GN: case GameVersion.YW:
return getStatic(pkm, StaticRBY, lvl);
@ -884,18 +891,31 @@ namespace PKHeX.Core
// Get Valid levels
IEnumerable<DexLevel> vs = getValidPreEvolutions(pkm);
// Get slots where pokemon can exist
IEnumerable<EncounterSlot> slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species && evo.Level >= slot.LevelMin - df) || ignoreLevel);
bool ignoreSlotLevel = ignoreLevel;
IEnumerable<EncounterSlot> slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species && (ignoreSlotLevel || evo.Level >= slot.LevelMin - df)));
if (pkm.Format < 3 || pkm.VC)
return slots; // no met level or special encounter considerations
// Filter for Met Level
int lvl = pkm.Met_Level;
var encounterSlots = slots.Where(slot => slot.LevelMin - df <= lvl && lvl <= slot.LevelMax + (slot.AllowDexNav ? dn : df) || ignoreLevel).ToList();
int gen = pkm.GenNumber;
bool ignoreMetLevel = ignoreLevel || gen <= 4 && pkm.Format != gen;
var encounterSlots = slots.Where(slot => ignoreMetLevel || slot.LevelMin - df <= lvl && lvl <= slot.LevelMax + (slot.AllowDexNav ? dn : df)).ToList();
// Pressure Slot
EncounterSlot slotMax = encounterSlots.OrderByDescending(slot => slot.LevelMax).FirstOrDefault();
if (slotMax != null)
slotMax = new EncounterSlot(slotMax) { Pressure = true, Form = pkm.AltForm };
if (gen < 4)
{
if (slotMax != null)
slotdata.Add(slotMax);
return slotdata;
}
if (!DexNav)
{
// Filter for Form Specific
@ -1005,7 +1025,11 @@ namespace PKHeX.Core
private static IEnumerable<int> getMoves(PKM pkm, int species, int lvl, int form, bool moveTutor, GameVersion Version, bool LVL, bool specialTutors, bool Machine, bool MoveReminder)
{
List<int> r = new List<int> { 0 };
for (int gen = pkm.GenNumber; gen <= pkm.Format; gen++)
int gen = pkm.GenNumber;
if (pkm.Format < 3)
gen = 1;
for (; gen <= pkm.Format; gen++)
r.AddRange(getMoves(pkm, species, lvl, form, moveTutor, Version, LVL, specialTutors, Machine, gen, MoveReminder));
return r.Distinct();
}
@ -1016,6 +1040,17 @@ namespace PKHeX.Core
var ver = Version;
switch (Generation)
{
case 1:
{
PersonalInfo pi = PersonalTable.RBY[species];
if (LVL)
{
r.AddRange(((PersonalInfoG1)PersonalTable.RBY[species]).Moves);
r.AddRange(LevelUpRB[species].getMoves(lvl));
}
if (Machine) r.AddRange(TMHM_RBY.Where((t, m) => pi.TMHM[m]));
break;
}
case 6:
switch (ver)
{

View file

@ -45,7 +45,67 @@ namespace PKHeX.Core
internal static readonly EncounterStatic[] Encounter_RBY =
{
// todo
new EncounterStatic { Species = 001, Level = 05 }, // Bulbasaur
new EncounterStatic { Species = 004, Level = 05 }, // Charmander
new EncounterStatic { Species = 007, Level = 05 }, // Squirtle
new EncounterStatic { Species = 025, Level = 05 }, // Pikachu
// Red Game Corner
new EncounterStatic { Species = 063, Level = 09 }, // Abra
new EncounterStatic { Species = 025, Level = 08 }, // Clefairy
new EncounterStatic { Species = 025, Level = 17 }, // Nidorina
new EncounterStatic { Species = 147, Level = 18 }, // Dratini
new EncounterStatic { Species = 123, Level = 25 }, // Scyther
new EncounterStatic { Species = 137, Level = 26 }, // Porygon
// Blue(EN) / Green(JP) Game Corner
new EncounterStatic { Species = 063, Level = 06 }, // Abra
new EncounterStatic { Species = 035, Level = 12 }, // Clefairy
new EncounterStatic { Species = 030, Level = 17 }, // Nidorina
new EncounterStatic { Species = 127, Level = 20 }, // Pinsir
new EncounterStatic { Species = 147, Level = 24 }, // Dratini
new EncounterStatic { Species = 137, Level = 18 }, // Porygon
// Blue(JP) Game Corner
new EncounterStatic { Species = 063, Level = 08 }, // Abra
new EncounterStatic { Species = 025, Level = 12 }, // Pikachu
new EncounterStatic { Species = 116, Level = 18 }, // Horsea
new EncounterStatic { Species = 036, Level = 24 }, // Clefable
new EncounterStatic { Species = 148, Level = 30 }, // Dragonair
new EncounterStatic { Species = 137, Level = 22 }, // Porygon
// Yellow Game Corner
new EncounterStatic { Species = 063, Level = 15 }, // Abra
new EncounterStatic { Species = 025, Level = 18 }, // Vulpix
new EncounterStatic { Species = 025, Level = 22 }, // Wigglytuff
new EncounterStatic { Species = 123, Level = 30 }, // Scyther
new EncounterStatic { Species = 127, Level = 30 }, // Pinsir
new EncounterStatic { Species = 137, Level = 26 }, // Porygon
new EncounterStatic { Species = 129, Level = 05 }, // Magikarp
new EncounterStatic { Species = 143, Level = 30 }, // Snorlax
new EncounterStatic { Species = 106, Level = 30 }, // Hitmonlee
new EncounterStatic { Species = 107, Level = 30 }, // Hitmonchan
new EncounterStatic { Species = 131, Level = 15 }, // Lapras
new EncounterStatic { Species = 138, Level = 30 }, // Omanyte
new EncounterStatic { Species = 140, Level = 30 }, // Kabuto
new EncounterStatic { Species = 142, Level = 30 }, // Aerodactyl
new EncounterStatic { Species = 144, Level = 50 }, // Articuno
new EncounterStatic { Species = 145, Level = 50 }, // Zapdos
new EncounterStatic { Species = 146, Level = 50 }, // Moltres
new EncounterStatic { Species = 150, Level = 70 }, // Mewtwo
new EncounterStatic { Species = 133, Level = 25 }, // Eevee
// Yellow Only
new EncounterStatic { Species = 133, Level = 25 }, // Eevee (Celadon City)
new EncounterStatic { Species = 001, Level = 10 }, // Bulbasaur (Cerulean City)
new EncounterStatic { Species = 004, Level = 10 }, // Charmander (Route 24)
new EncounterStatic { Species = 007, Level = 10 }, // Squirtle (Vermillion City)
};
internal static readonly EncounterTrade[] TradeGift_RBY =
{

View file

@ -259,7 +259,7 @@ namespace PKHeX.Core
public override int OT_Friendship { get { return 0; } set { } }
public override int OT_Gender { get { return 0; } set { } }
public override int Ball { get { return 0; } set { } }
public override int Version { get { return 0; } set { } }
public override int Version { get { return (int)GameVersion.RBY; } set { } }
public override int SID { get { return 0; } set { } }
public override int PKRS_Strain { get { return 0; } set { } }
public override int PKRS_Days { get { return 0; } set { } }

View file

@ -327,7 +327,7 @@ namespace PKHeX.Core
public override int Egg_Location { get { return 0; } set { } }
public override int OT_Friendship { get { return 0; } set { } }
public override int Ball { get { return 0; } set { } }
public override int Version { get { return 0; } set { } }
public override int Version { get { return (int)GameVersion.GSC; } set { } }
public override int SID { get { return 0; } set { } }
public override int CNT_Cool { get { return 0; } set { } }
public override int CNT_Beauty { get { return 0; } set { } }

View file

@ -275,7 +275,9 @@ namespace PKHeX.Core
public bool Gen5 => Version >= 20 && Version <= 23;
public bool Gen4 => Version >= 7 && Version <= 12 && Version != 9;
public bool Gen3 => Version >= 1 && Version <= 5 || Version == 15;
public bool GenU => !(Gen7 || Gen6 || Gen5 || Gen4 || Gen3);
public bool Gen2 => Version == (int)GameVersion.GSC;
public bool Gen1 => Version == (int)GameVersion.RBY;
public bool GenU => !(Gen7 || Gen6 || Gen5 || Gen4 || Gen3 || Gen2 || Gen1);
public int GenNumber
{
get
@ -286,6 +288,8 @@ namespace PKHeX.Core
if (Gen5) return 5;
if (Gen4) return 4;
if (Gen3) return 3;
if (Gen2) return Format; // 2
if (Gen1) return Format; // 1
return -1;
}
}
@ -405,14 +409,17 @@ namespace PKHeX.Core
public virtual bool WasEventEgg => ((Egg_Location > 40000 && Egg_Location < 50000) || (FatefulEncounter && Egg_Location > 0)) && Met_Level == 1;
public virtual bool WasTradedEgg => Egg_Location == 30002;
public virtual bool WasIngameTrade => Met_Location == 30001;
public virtual bool IsUntraded => string.IsNullOrWhiteSpace(HT_Name) && GenNumber == Format;
public virtual bool IsUntraded => Format >= 6 && string.IsNullOrWhiteSpace(HT_Name) && GenNumber == Format;
public virtual bool IsNative => GenNumber == Format;
public virtual bool IsOriginValid()
{
switch (GenNumber)
switch (Format)
{
case 1: return Species <= Legal.MaxSpeciesID_1;
case 2: return Species <= Legal.MaxSpeciesID_2;
}
switch (GenNumber)
{
case 3: return Species <= Legal.MaxSpeciesID_3;
case 4: return Species <= Legal.MaxSpeciesID_4;
case 5: return Species <= Legal.MaxSpeciesID_5;
@ -482,7 +489,7 @@ namespace PKHeX.Core
/// Checks if the PKM has its original met location.
/// </summary>
/// <returns>Returns false if the Met Location has been overwritten via generational transfer.</returns>
public bool HasOriginalMetLocation => !(GenNumber <= 4 && Format > 4 || VC);
public bool HasOriginalMetLocation => !(Format < 3 || VC || GenNumber <= 4 && Format != GenNumber);
/// <summary>
/// Checks if the current <see cref="Gender"/> is valid.

View file

@ -40,11 +40,11 @@ namespace PKHeX.Core
}
public override int CatchRate { get { return Data[0x08]; } set { Data[0x08] = (byte)value; } }
public override int BaseEXP { get { return Data[0x09]; } set { Data[0x09] = (byte)value; } }
public int Move1 { get { return Data[0x0A]; } set { Data[0x0A] = (byte)value; } }
public int Move2 { get { return Data[0x0B]; } set { Data[0x0B] = (byte)value; } }
public int Move3 { get { return Data[0x0C]; } set { Data[0x0C] = (byte)value; } }
public int Move4 { get { return Data[0x0D]; } set { Data[0x0D] = (byte)value; } }
public override int EXPGrowth { get { return Data[0x13]; } set { Data[0x13] = (byte)value; } }
public int Move1 { get { return Data[0x0F]; } set { Data[0x0F] = (byte)value; } }
public int Move2 { get { return Data[0x10]; } set { Data[0x10] = (byte)value; } }
public int Move3 { get { return Data[0x12]; } set { Data[0x12] = (byte)value; } }
public int Move4 { get { return Data[0x13]; } set { Data[0x13] = (byte)value; } }
public override int EXPGrowth { get { return Data[0x14]; } set { Data[0x14] = (byte)value; } }
// EV Yields are just aliases for base stats in Gen I
public override int EV_HP { get { return HP; } set { } }
@ -64,5 +64,11 @@ namespace PKHeX.Core
public override int BaseFriendship { get { return 0; } set { } }
public override int EscapeRate { get { return 0; } set { } }
public override int Color { get { return 0; } set { } }
public int[] Moves
{
get { return new[] { Move1, Move2, Move3, Move4 }; }
set { if (value?.Length != 4) return; Move1 = value[0]; Move2 = value[1]; Move3 = value[2]; Move4 = value[3]; }
}
}
}