Generation 1 and 2 legal Improvements (#1099)

* Refactor parseMovesForEncounter to gather valid moves for species encounter, some Pokemon can have valid encounters with different source species from the encounter, the valid moves change if the encounter species change because some preevolutions moves are illegal if pokemon caught already evolved.
Example, generation 1 Pikachu that can have a RBY Pikachu encounter and GSC Pichu encounter, the valid moves for the first encounters should not have any Pichu exclusive evolution moves

Also assign the encounter match from gb when parsing moves like the variable Encounter Match, to store the encounter that is valid for the pokemon moves instead the first encounter.

Store the species encounter, this will be needed to check if the evolution is valid for species that evolve leveling with a given learned move

* Add Tradeback Status to the pokemon, this variable for generations 1 and 2 use data like the catch rate to determine if trade between generations 1 and 2 was possible.
If analysis is for VC games tradeback have value NotTradeback for every gen 1 pokemon, but for cart saves some pokemon can be determine that have not been tradeback, if catch rate match species catch rate but do not match a valid generation 2 held item that means the pokemon habe been never traded to generation 2 games, that allow to discart encounters and moves from generation 2.

Also if is not tradeback catch rate is used to filter encounters, catch rate determine in what species was captured the pokemon discarting some preevolutions moves

Also add option for generation 1 cart save analysis to check legal status not allowing generation 2 games, like VC games but with Stadium allowed, like the generation 1 non tradeback rules from Smogon

Also change evolution chains to included generation 2 preevolutions for gen 1 pokemon if tradeback was possible, it is needed to avoid parsemoves to check illegal pokemon like Hitmonchan with Tyrogue level up moves

* Check legal values of generation 1 type and catch rate
Replace pokemon catch rate after changind pokemon species always if pokemon was not tradeback from generation 2, the catch rate will keep unchanged only if it can be a held item and do not match species catch rate (default item)
Also if catch rate is changed use base species catch rate to avoid legal errors if the catch rate of the evolution species if is not possible with the current moves

* Filter ingame trades and static encounters with catch rate for generation 1 non tradeback

* Fix min moves for generation 1 metapod encounter

* Clean up

* Fix encounter level for generation 1, valid moves are those with one level after the encounter level, pokemon can not learn a new move until level up
Clean up type validation
Fix generation 3 fatefull encounter eggs, the pokemon lost the fatefull mark when it hatch

* Clean-up

* Use new variable EncounterSpecies when it is needed to detect the species of the encounter, the old code wont work if the encounter is a wild slots array

* Fix generation 1 evolution chains and catch rate as default held item

* Fix Generation 1 Yellow Pikachu and Kadabra catch rates
This commit is contained in:
javierhimura 2017-04-27 06:27:59 +02:00 committed by Kurt
parent ece845cd17
commit b14ac67c1f
15 changed files with 476 additions and 110 deletions

View file

@ -977,9 +977,17 @@ namespace PKHeX.WinForms
if (drVC == DialogResult.Cancel)
return;
Legal.AllowGBCartEra = drVC == DialogResult.No; // physical cart selected
if (Legal.AllowGBCartEra && sav.Generation == 1)
{
var drTradeback = WinFormsUtil.Prompt(MessageBoxButtons.YesNoCancel, "Generation 1 Save File detected. Allow tradeback from generation 2 for legallity purpose?",
"Yes: Generation 2 tradeback allow" + Environment.NewLine + "No: Only consider legal pokemon possible without generation 2 games");
Legal.AllowGen1Tradeback = drTradeback == DialogResult.Yes;
}
else
Legal.AllowGen1Tradeback = false;
}
else
Legal.AllowGBCartEra = sav.Generation == 2;
Legal.AllowGBCartEra = Legal.AllowGen1Tradeback = sav.Generation == 2;
if (sav.Generation == 3 && (sav.IndeterminateGame || ModifierKeys == Keys.Control))
{

View file

@ -12,8 +12,9 @@ namespace PKHeX.Core
private readonly List<CheckResult> Parse = new List<CheckResult>();
private List<GBEncounterData> EncountersGBMatch;
private object EncounterOriginalGB => EncountersGBMatch?.FirstOrDefault()?.Encounter;
private object EncounterOriginalGB;
private object EncounterMatch;
private int EncounterSpecies;
private Type Type; // Parent class when applicable (EncounterStatic / MysteryGift)
private Type MatchedType; // Child class if applicable (WC6, PGF, etc)
private string EncounterName => Legal.getEncounterTypeName(pkm, EncounterOriginalGB ?? EncounterMatch);
@ -122,13 +123,14 @@ namespace PKHeX.Core
pkm = pk;
if (!pkm.IsOriginValid)
{ AddLine(Severity.Invalid, V187, CheckIdentifier.None); return; }
UpdateTradebackG12();
updateEncounterChain();
updateMoveLegality();
updateTypeInfo();
verifyNickname();
verifyDVs();
verifyG1OT();
verifyMiscG1();
}
private void parsePK3(PKM pk)
{
@ -212,6 +214,57 @@ namespace PKHeX.Core
Parse.Add(Encounter);
EvoChainsAllGens = Legal.getEvolutionChainsAllGens(pkm, EncounterOriginalGB ?? EncounterMatch);
}
private void UpdateTradebackG12()
{
if (pkm.Format == 1)
{
if (!Legal.AllowGen1Tradeback)
{
pkm.TradebackStatus = TradebackType.Gen1_NotTradeback;
(pkm as PK1).CatchRateIsItem = false;
}
else
{
var catch_rate = (pkm as PK1).Catch_Rate;
// If catch rate match a species catch rate from the evolution chain of the species but do not match a generation 2 item means it was not tradeback
// If match a held item but not a species catch rate then in means it was tradeback
var HeldItemCatchRate = (catch_rate == 0 || Legal.HeldItems_GSC.Any(h => h == catch_rate));
// For species catch rate discart species that have no valid encounters and different catch rate that their preevolutions
var Lineage = Legal.getLineage(pkm).Where(s => !Legal.Species_NotAvailable_CatchRate.Contains(s)).ToList();
var RGBCatchRate = Lineage.Any(s => catch_rate == PersonalTable.RB[s].CatchRate);
// Dragonite Catch Rate is different than Dragonair in Yellow but there is not any Dragonite encounter
var YCatchRate = Lineage.Any(s => s != 149 && catch_rate == PersonalTable.Y[s].CatchRate);
if (HeldItemCatchRate && !RGBCatchRate && !YCatchRate)
pkm.TradebackStatus = TradebackType.WasTradeback;
else if (!HeldItemCatchRate && (RGBCatchRate || YCatchRate))
pkm.TradebackStatus = TradebackType.Gen1_NotTradeback;
else
pkm.TradebackStatus = TradebackType.Any;
// Set CatchRateIsItem to true to keep the held item stored when changed species
// only if catch rate match a valid held item and do not match the default catch rate from the species
// If pokemon have not been traded to gen 2 then catch rate could not be a held item
(pkm as PK1).CatchRateIsItem = pkm.Gen1_NotTradeback ? false : (HeldItemCatchRate && !RGBCatchRate && !YCatchRate);
}
}
else if (pkm.Format == 2 || pkm.VC2)
{
// Eggs, pokemon with non-empty crystal met location and generation 2 species without generation 1 preevolutions can not be traded to generation 1 games
if (pkm.IsEgg || pkm.HasOriginalMetLocation || (pkm.Species > Legal.MaxSpeciesID_1 && !Legal.FutureEvolutionsGen1.Contains(pkm.Species)))
pkm.TradebackStatus = TradebackType.Gen2_NotTradeback;
else
pkm.TradebackStatus = TradebackType.Any;
}
else if (pkm.VC1)
{
// Probably if VC2 is released VC1 pokemon with met date after VC2 Bank release date will be TradebackType.Any
pkm.TradebackStatus = TradebackType.Gen1_NotTradeback;
}
else
{
pkm.TradebackStatus = TradebackType.Any;
}
}
private void updateTypeInfo()
{
if (pkm.VC && pkm.Format == 7)

View file

@ -773,7 +773,7 @@ namespace PKHeX.Core
}
private CheckResult verifyEncounterG12()
{
EncountersGBMatch = Legal.getEncounter12(pkm, Legal.AllowGBCartEra && pkm.Format < 3);
EncountersGBMatch = Legal.getEncounter12(pkm);
if (EncountersGBMatch == null)
return new CheckResult(Severity.Invalid, V80, CheckIdentifier.Encounter);
@ -782,7 +782,7 @@ namespace PKHeX.Core
pkm.WasEgg = true;
return verifyEncounterEgg();
}
EncounterMatch = EncounterOriginalGB;
EncounterMatch = EncounterOriginalGB = EncountersGBMatch?.FirstOrDefault()?.Encounter;
if (EncounterMatch is EncounterSlot)
return new CheckResult(Severity.Valid, V68, CheckIdentifier.Encounter);
if (EncounterMatch is EncounterStatic)
@ -2339,6 +2339,64 @@ namespace PKHeX.Core
AddLine(Severity.Valid, V318, CheckIdentifier.Form);
}
private void verifyMiscG1()
{
if (pkm.Format > 1)
return;
var Type_A = (pkm as PK1).Type_A;
var Type_B = (pkm as PK1).Type_B;
if (pkm.Species == 137)
{
// Porygon can have any type combination of any generation 1 species because of the move Conversion,
// that change Porygon type to match the oponent types
var Type_A_Match = Legal.Types_Gen1.Any(t => t == Type_A);
var Type_B_Match = Legal.Types_Gen1.Any(t => t == Type_B);
if (!Type_A_Match)
AddLine(Severity.Invalid, V386, CheckIdentifier.Misc);
if (!Type_B_Match)
AddLine(Severity.Invalid, V387, CheckIdentifier.Misc);
if (Type_A_Match && Type_B_Match)
{
var TypesAB_Match = PersonalTable.RB.IsValidTypeCombination(Type_A, Type_B);
if (TypesAB_Match)
AddLine(Severity.Valid, V391, CheckIdentifier.Misc);
else
AddLine(Severity.Invalid, V388, CheckIdentifier.Misc);
}
}
else // Types must match species types
{
var Type_A_Match = Type_A == PersonalTable.RB[pkm.Species].Types[0];
var Type_B_Match = Type_B == PersonalTable.RB[pkm.Species].Types[1];
AddLine(Type_A_Match ? Severity.Valid : Severity.Invalid, Type_A_Match ? V392 : V389, CheckIdentifier.Misc);
AddLine(Type_B_Match ? Severity.Valid : Severity.Invalid, Type_B_Match ? V393 : V390, CheckIdentifier.Misc);
}
var catch_rate =(pkm as PK1).Catch_Rate;
switch (pkm.TradebackStatus)
{
case TradebackType.Any:
case TradebackType.WasTradeback:
if (catch_rate == 0 || Legal.HeldItems_GSC.Any(h => h == catch_rate))
{ AddLine(Severity.Valid, V394, CheckIdentifier.Misc); }
else if (pkm.TradebackStatus == TradebackType.WasTradeback)
{ AddLine(Severity.Invalid, V395, CheckIdentifier.Misc); }
else
goto case TradebackType.Gen1_NotTradeback;
break;
case TradebackType.Gen1_NotTradeback:
if ( ((pkm.Species == 149) && (catch_rate == PersonalTable.Y[149].CatchRate)) ||
(Legal.Species_NotAvailable_CatchRate.Contains(pkm.Species) && (catch_rate == PersonalTable.RB[pkm.Species].CatchRate)))
{ AddLine(Severity.Invalid, V396, CheckIdentifier.Misc); }
else if (!EvoChainsAllGens[1].Any(e => catch_rate == PersonalTable.RB[e.Species].CatchRate || catch_rate == PersonalTable.Y[e.Species].CatchRate))
{ AddLine(Severity.Invalid, pkm.Gen1_NotTradeback? V397: V399, CheckIdentifier.Misc); }
else
{ AddLine(Severity.Valid, V398, CheckIdentifier.Misc); }
break;
}
}
private void verifyMisc()
{
if (pkm.Format == 7 && ((PK7)pkm).PelagoEventStatus != 0)
@ -2374,7 +2432,11 @@ namespace PKHeX.Core
if (Type == typeof(EncounterStatic))
{
var enc = EncounterMatch as EncounterStatic;
if (enc.Fateful)
var fateful = enc.Fateful;
if (pkm.Gen3 && pkm.WasEgg && !pkm.IsEgg)
// Fatefull generation 3 eggs lost fatefull mark after hatch
fateful = false;
if (fateful)
{
if (pkm.FatefulEncounter)
AddLine(Severity.Valid, V323, CheckIdentifier.Fateful);
@ -2508,15 +2570,8 @@ namespace PKHeX.Core
}
private CheckResult[] verifyMoves(GameVersion game = GameVersion.Any)
{
int minLvLG1 = 0;
if (pkm.GenNumber <= 2)
minLvLG1 = pkm.WasEgg ? 6 : (EncounterMatch as IEncounterable)?.LevelMin + 1 ?? 0;
var validLevelMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, minLvLG1: minLvLG1, Tutor: false, Machine: false, RemoveTransferHM: false);
var validTMHM = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Tutor: false, MoveReminder: false, RemoveTransferHM: false);
var validTutor = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Machine: false, MoveReminder: false, RemoveTransferHM: false);
Legal.RemoveFutureMoves(pkm, ref validLevelMoves, ref validTMHM, ref validTutor);
int[] Moves = pkm.Moves;
var res = parseMovesForEncounters(game, validLevelMoves, validTMHM, validTutor, Moves);
var res = parseMovesForEncounters(game, Moves);
// Duplicate Moves Check
verifyNoEmptyDuplicates(Moves, res);
@ -2525,63 +2580,129 @@ namespace PKHeX.Core
return res;
}
private void UptateGen1LevelUpMoves(ref List<int>[] validLevelMoves, List<int> DefaultMoves, int generation)
private void UptateGen1LevelUpMoves(ValidEncounterMoves EncounterMoves, int defaultLvlG1, int generation)
{
switch (generation)
switch(generation)
{
case 1:
validLevelMoves[1] = Legal.getValidMoves(pkm, EvoChainsAllGens[1], generation: 1, minLvLG1: (EncounterMatch as IEncounterable).LevelMin, LVL: true, Tutor: false, Machine: false, MoveReminder: false).ToList();
break;
case 2:
validLevelMoves[1] = DefaultMoves;
var lvlG1 = EncounterMatch == null ? 6 : (EncounterMatch as IEncounterable).LevelMin + 1;
if (lvlG1 != defaultLvlG1)
EncounterMoves.validLevelUpMoves[1] = Legal.getValidMoves(pkm, EvoChainsAllGens[1], generation: 1, minLvLG1: lvlG1, LVL: true, Tutor: false, Machine: false, MoveReminder: false).ToList();
break;
}
}
private CheckResult[] parseMovesForEncounters(GameVersion game, List<int>[] validLevelMoves, List<int>[] validTMHM, List<int>[] validTutor, int[] Moves)
private ValidEncounterMoves getEncounterValidMoves(int defaultspecies, int encounterspecies, object encounter, int encounterlevel)
{
var minLvLG1 = pkm.GenNumber <= 2 ? encounterlevel + 1 : 0;
// If encounter species is the same species from the first match, the one in variable EncounterMatch, its evolution chains is already in EvoChainsAllGens
var EvolutionChains = defaultspecies == EncounterSpecies ? EvoChainsAllGens : Legal.getEvolutionChainsAllGens(pkm, encounter);
var LevelMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, minLvLG1: minLvLG1, Tutor: false, Machine: false, RemoveTransferHM: false);
var TMHMMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Tutor: false, MoveReminder: false, RemoveTransferHM: false);
var TutorMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Machine: false, MoveReminder: false, RemoveTransferHM: false);
return new ValidEncounterMoves()
{
EncounterSpecies = encounterspecies,
validLevelUpMoves = LevelMoves,
validTMHMMoves = TMHMMoves,
validTutorMoves = TutorMoves,
EvolutionChains = EvolutionChains,
minLvlG1 = minLvLG1
};
}
private List<ValidEncounterMoves> getEncountersValidMoves(List<object> encounters, DexLevel[] vs)
{
var defaultspecies = Legal.getEncounterSpecies(pkm, vs, EncounterMatch);
var r = new List<ValidEncounterMoves>();
foreach(DexLevel evo in vs)
{
// Store only one set of valid moves for species, using the minimun level encounter for that species
var encounters_evo = encounters.Where(e => Legal.getEncounterSpecies(pkm, vs, e) == evo.Species);
if (!encounters_evo.Any())
continue;
// For every possible encounter species get valid moves using minimun encounter level for each species
// Generation 1 encounters will overwrite the valid level moves of gen 1 if encounter level is not the minimun
var minlevel = encounters_evo.Min(e => Legal.getEncounterLevel(pkm, e));
var encounter_minlevel = encounters_evo.First(e => Legal.getEncounterLevel(pkm, e) == minlevel);
r.Add(getEncounterValidMoves(defaultspecies, evo.Species, encounter_minlevel, minlevel));
}
return r;
}
private CheckResult[] parseMovesForEncounters(GameVersion game, int[] Moves)
{
if (pkm.Species == 235) // special handling for Smeargle
return parseMovesForSmeargle(Moves, validLevelMoves); // Smeargle can have any moves except a few
return parseMovesForSmeargle(Moves); // Smeargle can have any moves except a few
// Gather Encounters
var encounters = pkm.GenNumber <= 2 ? GetEncounterMovesGBEra() :
pkm.GenNumber == 3 && pkm.WasEgg ? GetEncounterMovesGen3Egg() :
GetEncounterMoves();
// it could be duplicated between EncounterMatch and EncounterStaticMatch or EncounterMatch and EventGiftMatch
encounters = encounters.Distinct().ToList();
if (!encounters.Any()) // There isn't any valid encounter and wasnt an egg
{
var empty = Legal.GetEmptyMovesList(EvoChainsAllGens);
var emptyegg = Legal.GetEmptyEggMovesList();
return parseMoves(pkm.Moves, validLevelMoves, pkm.RelearnMoves, validTMHM, validTutor, new int[0], emptyegg, emptyegg, empty, new int[0], new int[0], false);
return parseMovesNoEncounters(Moves);
}
// Iterate over encounters
bool pre3DS = pkm.GenNumber < 6;
var vs = Legal.getValidPreEvolutions(pkm).ToArray();
// gather valid moves for encounter species
var EncountersMoves = getEncountersValidMoves(encounters, vs);
CheckResult[] res = new CheckResult[4];
var defaultGen1Moves = validLevelMoves[1];
foreach (var enc in encounters)
{
EncounterMatch = enc;
var EncounterMatchGen = EncounterMatch as IGeneration;
if (EncounterMatchGen != null)
UptateGen1LevelUpMoves(ref validLevelMoves, defaultGen1Moves, EncounterMatchGen.Generation);
if (pkm.GenNumber <= 2)
EncounterOriginalGB = enc;
EncounterSpecies = Legal.getEncounterSpecies(pkm, vs, EncounterMatch);
var EncounterMoves = EncountersMoves.First(e => e.EncounterSpecies == EncounterSpecies);
EvoChainsAllGens = EncounterMoves.EvolutionChains;
if (pkm.GenNumber <= 3)
pkm.WasEgg = EncounterMatch == null || ((EncounterMatch as IEncounterable)?.EggEncounter ?? false);
pkm.WasEgg = (EncounterMatch == null) || ((EncounterMatch as IEncounterable)?.EggEncounter ?? false);
var EncounterMatchGen = EncounterMatch as IGeneration;
var defaultG1LevelMoves = EncounterMoves.validLevelUpMoves[1];
if (EncounterMatchGen != null)
// Generation 1 can have different minimun level in different encounter of the same species, update valid level moves
UptateGen1LevelUpMoves(EncounterMoves, EncounterMoves.minLvlG1, EncounterMatchGen.Generation);
res = pre3DS
? parseMovesPre3DS(game, validLevelMoves, validTMHM, validTutor, Moves)
: parseMoves3DS(game, validLevelMoves, validTMHM, validTutor, Moves);
? parseMovesPre3DS(game, EncounterMoves.validLevelUpMoves, EncounterMoves.validTMHMMoves, EncounterMoves.validTutorMoves, Moves)
: parseMoves3DS(game, EncounterMoves.validLevelUpMoves, EncounterMoves.validTMHMMoves, EncounterMoves.validTutorMoves, Moves);
if (res.All(x => x.Valid))
break;
if (EncounterMatchGen?.Generation == 1)
// If is not valid restore generation 1 moves
EncounterMoves.validLevelUpMoves[1] = defaultG1LevelMoves;
}
return res;
}
private CheckResult[] parseMovesForSmeargle(int[] Moves, List<int>[] validLevelMoves)
private CheckResult[] parseMovesNoEncounters(int[] Moves)
{
EncounterSpecies = pkm.Species;
var validLevelMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, minLvLG1: 1, Tutor: false, Machine: false, RemoveTransferHM: false);
var validTMHM = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Tutor: false, MoveReminder: false, RemoveTransferHM: false);
var validTutor = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, LVL: false, Machine: false, MoveReminder: false, RemoveTransferHM: false);
Legal.RemoveFutureMoves(pkm, ref validLevelMoves, ref validTMHM, ref validTutor);
var empty = Legal.GetEmptyMovesList(EvoChainsAllGens);
var emptyegg = Legal.GetEmptyEggMovesList();
return parseMoves(pkm.Moves, validLevelMoves, pkm.RelearnMoves, validTMHM, validTutor, new int[0], emptyegg, emptyegg, empty, new int[0], new int[0], false);
}
private CheckResult[] parseMovesForSmeargle(int[] Moves)
{
if (!pkm.IsEgg)
return parseMovesSketch(Moves);
var validLevelMoves = Legal.getValidMovesAllGens(pkm, EvoChainsAllGens, minLvLG1: 1, Tutor: false, Machine: false, RemoveTransferHM: false);
// can only know sketch as egg
var empty = Legal.GetEmptyMovesList(EvoChainsAllGens);
var emptyegg = Legal.GetEmptyEggMovesList();
@ -2716,7 +2837,7 @@ namespace PKHeX.Core
Games = new[] { GameVersion.B2W2 };
break;
}
var issplitbreed = Legal.SplitBreed.Contains(pkm.Species);
var issplitbreed = Legal.getSplitBreedGeneration(pkm).Contains(pkm.Species);
foreach (var ver in Games)
{
var EventEggMoves = (EncounterMatch as IMoveset)?.Moves ?? new int[0];
@ -3034,6 +3155,11 @@ namespace PKHeX.Core
// Example: Azurill levelup/tmhm/tutor move, incompatible with Marill egg moves
res[m] = new CheckResult(Severity.Invalid, string.Format(V376, splitbreedspecies0, splitbreedspecies1), CheckIdentifier.Move);
}
else if (EncounterMatch == null && !EggMovesSplitLearned[0].Any() && !IncenseMovesLearned.Any())
{
// Day care egg with no incense species exclusive moves, we can assume non-incense egg, that means there is no need to check if evolution from incense species is valid
EncounterSpecies = Legal.getBaseEggSpecies(pkm, 1);
}
}
}
@ -3062,7 +3188,7 @@ namespace PKHeX.Core
foreach (int m in Gen1MovesLearned)
res[m] = new CheckResult(Severity.Invalid, V335, CheckIdentifier.Move);
if (gen == 1 && pkm.Format == 1 && !Legal.AllowGBCartEra)
if (gen == 1 && pkm.Format == 1 && pkm.Gen1_NotTradeback)
{
// Check moves learned at the same level in red/blue and yellow, illegal because there is no move reminder
// Only two incompatibilites and only there are no illegal combination if generation 2 or 7 are included in the analysis
@ -3083,7 +3209,7 @@ namespace PKHeX.Core
return res;
}
if (pkm.Species == 292 && (EncounterMatch as IEncounterable)?.Species != 292)
if (pkm.Species == 292 && EncounterSpecies != 292)
{
// Ignore Shedinja if the Encounter was also a Shedinja, assume null Encounter as a Nincada egg
// Check Shedinja evolved moves from Ninjask after egg moves
@ -3209,7 +3335,7 @@ namespace PKHeX.Core
// Ignore if there is an invalid move or an empty move, this validtion is only for 4 non-empty moves that are all valid, but invalid as a 4 combination
// Ignore Mr.Mime and Sodowodoo from generations 1 to 3, they cant be evolved from Bonsly or Munchlax
// Ignore if encounter species is the evolution species, pokemon was not evolved by the player
if (!res.All(r => r?.Valid ?? false) || moves.Any(m => m == 0) || (Legal.BabyEvolutionWithMove.Contains(pkm.Species) && pkm.GenNumber <= 3) || (EncounterMatch as IEncounterable)?.Species == pkm.Species)
if (!res.All(r => r?.Valid ?? false) || moves.Any(m => m == 0) || (Legal.BabyEvolutionWithMove.Contains(pkm.Species) && pkm.GenNumber <= 3) || EncounterSpecies == pkm.Species)
return;
// Mr.Mime and Sodowodoo from eggs that does not have any exclusive egg move or level up move from Mime Jr or Bonsly, egg can be assumed to be a non-incense egg, pokemon was not evolved by the player

View file

@ -14,6 +14,7 @@ namespace PKHeX.Core
/// <summary>Setting to specify if an analysis should permit data sourced from the physical cartridge era of GameBoy games.</summary>
public static bool AllowGBCartEra = false;
public static bool AllowGen1Tradeback = false;
/// <summary>Setting to specify if the e-berry index item is an eningma berry or a e-reader berry and the name of the e-reader berry</summary>
public static bool EReaderBerryIsEnigma = true;
@ -554,6 +555,7 @@ namespace PKHeX.Core
MarkEncountersGeneration(ref SlotsRBY, 1);
MarkEncountersGeneration(ref StaticRBY, 1);
Evolves1 = new EvolutionTree(new[] { Resources.evos_rby }, GameVersion.RBY, PersonalTable.Y, MaxSpeciesID_1);
FixPersonalTableY();
}
// Gen 2
{
@ -758,6 +760,13 @@ namespace PKHeX.Core
}
}
private static void FixPersonalTableY()
{
// Personal Table from Yellow do not have yellow catch rate for Pikachu and Kadabra, have RedBlue instead
PersonalTable.Y[25].CatchRate = 163; // Pikachu
PersonalTable.Y[64].CatchRate = 96; // Kadabra
}
// Moves
internal static int[] getMinLevelLearnMove(int species, int Generation, List<int> moves)
{
@ -994,7 +1003,7 @@ namespace PKHeX.Core
}
}
}
else
else if (validLevelMoves.Length >= 3)
{
int tradeback = pkm.Format == 2 ? 1 : 2;
validLevelMoves[tradeback]?.RemoveAll(x => FutureMoves.Contains(x));
@ -1227,7 +1236,7 @@ namespace PKHeX.Core
}
internal static List<int>[] getBaseEggMoves(PKM pkm, GameVersion gameSource, int lvl)
{
if (SplitBreed.Contains(pkm.Species))
if (getSplitBreedGeneration(pkm.GenNumber).Contains(pkm.Species))
return new[]
{
getBaseEggMoves(pkm, 0, gameSource,lvl).ToList(),
@ -1237,7 +1246,7 @@ namespace PKHeX.Core
}
internal static List<int>[] getEggMoves(PKM pkm, GameVersion Version)
{
if (SplitBreed.Contains(pkm.Species))
if (getSplitBreedGeneration(pkm.GenNumber).Contains(pkm.Species))
return new[]
{
getEggMoves(pkm, getBaseEggSpecies(pkm, 0), 0, Version).ToList(),
@ -1388,6 +1397,18 @@ namespace PKHeX.Core
if (e.Form != pkm.AltForm && !e.SkipFormCheck && !getCanFormChange(pkm, e.Species))
continue;
if (pkm.Format == 1 && pkm.Gen1_NotTradeback)
{
var catch_rate = (pkm as PK1).Catch_Rate;
// Pure gen 1, trades can be filter by catch rate
if ((pkm.Species == 25 || pkm.Species == 26) && catch_rate == 190)
// Red Blue Pikachu, is not a static encounter
continue;
if (catch_rate != PersonalTable.RB[e.Species].CatchRate && catch_rate != PersonalTable.Y[e.Species].CatchRate)
continue;
}
// Defer to EC/PID check
// if (e.Shiny != null && e.Shiny != pkm.IsShiny)
// continue;
@ -1464,14 +1485,17 @@ namespace PKHeX.Core
}
internal static GameVersion[] getGen1GameEncounter(PKM pk)
{
if (pk.Format != 2 || AllowGBCartEra)
if (pk.Format != 1 || !pk.Gen1_NotTradeback)
return new[] { GameVersion.RD, GameVersion.YW };
if (25 <= pk.Species && pk.Species <= 26)
// Yellow Pikachu detected by its special catch rate
return new[] { (((PK1)pk).Catch_Rate == 163) ? GameVersion.YW : GameVersion.RD };
return new[] { ((pk as PK1).Catch_Rate == 163) ? GameVersion.YW : GameVersion.RD };
if (64 <= pk.Species && pk.Species <= 65)
// Yellow Kadabra detected by its special catch rate
return new[] { (((PK1)pk).Catch_Rate == 96) ? GameVersion.YW : GameVersion.RD };
return new[] { ((pk as PK1).Catch_Rate == 96) ? GameVersion.YW : GameVersion.RD };
if (148 <= pk.Species && pk.Species <= 149 && ((pk as PK1).Catch_Rate == 27))
// Yellow Dragonair detected by its special catch rate, is have another catch rate could be red/blue dratini or yellow dratini
return new[] { GameVersion.YW };
return new[] { GameVersion.RD, GameVersion.YW };
}
internal static IEnumerable<int> getInitialMovesGBEncounter(int species, int lvl, GameVersion ver)
@ -1592,7 +1616,7 @@ namespace PKHeX.Core
}
internal static int getRequiredMoveCount(PKM pk, int[] moves, List<int>[] learn, List<int>[] tmhm, List<int>[] tutor, int[] initialmoves)
{
if (pk.Format != 1 || AllowGBCartEra) // No MoveDeleter
if (pk.Format != 1 || !pk.Gen1_NotTradeback) // No MoveDeleter
return 1; // Move deleter exits, slots from 2 onwards can allways be empty
int required = getRequiredMoveCount(pk, moves, learn, initialmoves);
@ -1618,20 +1642,22 @@ namespace PKHeX.Core
private static int getRequiredMoveSlotsRegular(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
{
int species = pk.Species;
int catch_rate = (pk as PK1).Catch_Rate;
// Caterpie and Metapod evolution lines have different count of possible slots available if captured in different evolutionary phases
// Example: a level 7 caterpie evolved into metapod will have 3 learned moves, a captured metapod will have only 1 move
if (010 == species || species == 011)
if ((species == 011 || species == 012) && catch_rate == 120)
{
if (!moves.Any(m => G1MetapodMoves.Contains(m))) // Captured as Metapod without Caterpie moves
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1MetapodMoves.Contains(lm));
// Captured as Metapod without Caterpie moves
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1CaterpieMoves.Contains(lm));
// There is no valid Butterfree encounter in generation 1 games
}
if (species == 014 || species == 015)
if ((species == 014 || species == 015) && (catch_rate == 45 || catch_rate == 120))
{
if (species == 15 && !moves.Any(m => G1KakunaMoves.Contains(m))) // Captured as Beedril without Weedle and Kakuna moves
if (species == 15 && catch_rate == 45) // Captured as Beedril without Weedle and Kakuna moves
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1KakunaMoves.Contains(lm));
if (!moves.Any(m => G1WeedleMoves.Contains(m))) // Captured as Kakuna without Weedle moves
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1WeedleMoves.Contains(lm));
// Captured as Kakuna without Weedle moves
return initialmoves.Union(learn[1]).Distinct().Count(lm => lm != 0 && !G1WeedleMoves.Contains(lm));
}
return getRequiredMoveCountSpecies3(species, pk.CurrentLevel, moves) ? 3 : 0; // no match
@ -1656,6 +1682,7 @@ namespace PKHeX.Core
}
private static int getRequiredMoveCountDecrement(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
{
int catch_rate = (pk as PK1).Catch_Rate;
int usedslots = initialmoves.Union(learn[1]).Where(m => m != 0).Distinct().Count();
// Yellow optional moves, reduce usedslots if the yellow move is not present
// The count wont go bellow 1 because the yellow moves were already counted and are not the only initial or level up moves
@ -1672,9 +1699,9 @@ namespace PKHeX.Core
if (064 == pk.Species || pk.Species == 065)
{
if (!moves.Contains(134))// Initial Yellow Kadabra Kinesis
if (catch_rate != 100)// Initial Yellow Kadabra Kinesis (move 134)
usedslots--;
if (pk.CurrentLevel < 10 && !moves.Contains(50)) // Kadabra Disable, not learned until 20 if captured as Abra
if (catch_rate == 200 && pk.CurrentLevel < 20) // Kadabra Disable, not learned until 20 if captured as Abra (move 50)
usedslots--;
}
if (104 == pk.Species || pk.Species == 105) // Cubone and Marowak
@ -1741,7 +1768,8 @@ namespace PKHeX.Core
switch (gameSource)
{
case GameVersion.RBY:
return getValidEncounterTradeVC1(pkm, p, TradeGift_RBY);
var table = !AllowGen1Tradeback ? TradeGift_RBY_NoTradeback : TradeGift_RBY_Tradeback;
return getValidEncounterTradeVC1(pkm, p, table);
case GameVersion.GSC:
return getValidEncounterTradeVC2(pkm, p);
default:
@ -1750,13 +1778,8 @@ namespace PKHeX.Core
}
private static EncounterTrade getValidEncounterTradeVC2(PKM pkm, DexLevel[] p)
{
// Check RBY trades with loosened level criteria.
var z = getValidEncounterTradeVC1(pkm, p, TradeGift_RBY_2);
if (z != null)
return z;
// Check GSC trades. Reuse generic table fetch-match
z = getValidEncounterTradeVC1(pkm, p, TradeGift_GSC);
var z = getValidEncounterTradeVC1(pkm, p, TradeGift_GSC);
// Filter Criteria
if (z?.Gender != pkm.Gender)
@ -1781,12 +1804,19 @@ namespace PKHeX.Core
return null;
if (z.Level > pkm.CurrentLevel) // minimum required level
return null;
if(pkm.Format == 1 && pkm.Gen1_NotTradeback)
{
// Even if the in game trade use the tables with source pokemon allowing generaion 2 games, the traded pokemon could be a non-tradeback pokemon
var catch_rate = (pkm as PK1).Catch_Rate;
if (catch_rate != PersonalTable.RB[z.Species].CatchRate && catch_rate != PersonalTable.Y[z.Species].CatchRate)
return null;
}
return z;
}
private static GBEncounterData getEncounter12(PKM pkm, GameVersion game)
{
var gen = game == GameVersion.GSC ? 2 : 1;
bool WasEgg = game == GameVersion.GSC && getWasEgg23(pkm) && !NoHatchFromEgg.Contains(pkm.Species);
bool WasEgg = !pkm.Gen1_NotTradeback && game == GameVersion.GSC && getWasEgg23(pkm) && !NoHatchFromEgg.Contains(pkm.Species);
if (WasEgg)
{
// Further Filtering
@ -1835,10 +1865,10 @@ namespace PKHeX.Core
return new GBEncounterData(pkm, gen, t);
return null;
}
internal static List<GBEncounterData> getEncounter12(PKM pkm, bool gen2)
internal static List<GBEncounterData> getEncounter12(PKM pkm)
{
var g1 = pkm.IsEgg || pkm.HasOriginalMetLocation ? null : getEncounter12(pkm, GameVersion.RBY);
var g2 = gen2 ? getEncounter12(pkm, GameVersion.GSC) : null;
var g1 = pkm.Gen2_NotTradeback ? null : getEncounter12(pkm, GameVersion.RBY);
var g2 = pkm.Gen1_NotTradeback ? null : getEncounter12(pkm, GameVersion.GSC);
if (g1 == null && g2 == null)
return null;
if (g1 == null || g2 == null)
@ -2591,9 +2621,10 @@ namespace PKHeX.Core
internal static DexLevel[][] getEvolutionChainsAllGens(PKM pkm, object Encounter)
{
var CompleteEvoChain = getEvolutionChain(pkm, Encounter).ToArray();
int size = Math.Max(pkm.Format, 2);
DexLevel[][] GensEvoChains = new DexLevel[size + 1][];
for (int i = 0; i <= size; i++)
int maxgen = pkm.Format == 1 && !pkm.Gen1_NotTradeback ? 2 : pkm.Format;
int mingen = pkm.Format == 2 && !pkm.Gen2_NotTradeback ? 1 : pkm.GenNumber;
DexLevel[][] GensEvoChains = new DexLevel[maxgen + 1][];
for (int i = 0; i <= maxgen; i++)
GensEvoChains[i] = new DexLevel[0];
if (pkm.Species == 0 || pkm.Format > 2 && pkm.GenU) // Illegal origin or empty pokemon, return only chain for current format
@ -2611,15 +2642,13 @@ namespace PKHeX.Core
}
int lvl = pkm.CurrentLevel;
int maxgen = pkm.Format <= 2 ? 2 : pkm.Format;
int mingen = pkm.VC2 || pkm.Format <= 2 ? 1 : pkm.GenNumber;
// Iterate generations backwards because level will be decreased from current level in each generation
for (int gen = maxgen; gen >= mingen; gen--)
{
if ((pkm.Gen1 || pkm.VC1) && pkm.Format > 2 && 2 <= gen && gen <= 6)
if (pkm.GenNumber == 1 && pkm.Gen1_NotTradeback && gen == 2)
continue;
if ((pkm.Gen2 || pkm.VC2) && 3 <= gen && gen <= 6)
if (pkm.GenNumber <= 2 && 3 <= gen && gen <= 6)
continue;
if (!pkm.HasOriginalMetLocation && pkm.Format > 2 && gen < pkm.Format && gen <= 4 && lvl > pkm.Met_Level)
{
@ -2660,6 +2689,10 @@ namespace PKHeX.Core
//For example a gen3 charizar in format 7 with current level 36 and met level 36
//chain level for charmander is 35, is bellow met level
GensEvoChains[gen] = GensEvoChains[gen].Where(e => e.Level >= getMinLevelGeneration(pkm,gen)).ToArray();
if (gen == 1 && GensEvoChains[gen].LastOrDefault()?.Species > MaxSpeciesID_1)
// Remove generation 2 pre-evolutions
GensEvoChains[gen] = GensEvoChains[gen].Take(GensEvoChains[gen].Length - 1).ToArray();
}
return GensEvoChains;
}
@ -2913,6 +2946,26 @@ namespace PKHeX.Core
// Get Valid levels
IEnumerable<DexLevel> vs = getValidPreEvolutions(pkm, maxspeciesorigin: maxspeciesorigin, lvl: ignoreLevel ? 100 : -1, skipChecks:ignoreLevel);
bool IsRGBKadabra = false;
if (pkm.Format == 1 && pkm.Gen1_NotTradeback)
{
// Pure gen 1, slots can be filter by catch rate
if ((pkm.Species == 25 || pkm.Species == 26) && (pkm as PK1).Catch_Rate == 163)
// Yellow Pikachu, is not a wild encounter
return slotdata;
if ((pkm.Species == 64 || pkm.Species == 65) && (pkm as PK1).Catch_Rate == 96)
// Yellow Kadabra, ignore Abra encounters
vs = vs.Where(s => s.Species == 64);
if ((pkm.Species == 148 || pkm.Species == 149) && (pkm as PK1).Catch_Rate == 27)
// Yellow Dragonair, ignore Dratini encounters
vs = vs.Where(s => s.Species == 148);
else
{
IsRGBKadabra = (pkm.Species == 64 || pkm.Species == 65) && (pkm as PK1).Catch_Rate == 100;
vs = vs.Where(s => (pkm as PK1).Catch_Rate == PersonalTable.RB[s.Species].CatchRate);
}
}
// Get slots where pokemon can exist
bool ignoreSlotLevel = ignoreLevel;
IEnumerable<EncounterSlot> slots = loc.Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species && (ignoreSlotLevel || evo.Level >= slot.LevelMin - df)));
@ -2931,7 +2984,11 @@ namespace PKHeX.Core
encounterSlots = slots.Where(slot => slot.LevelMin <= lvl).ToList();
if (gen <= 2)
{
{
if (IsRGBKadabra)
//Red Kadabra slots : Level 49 and 51 in RGB, but level 20 and 27 in Yellow
encounterSlots = encounterSlots.Where(slot => slot.LevelMin >= 49).ToList();
// For gen 1 and 2 return Minimum level slot
// Minimum level is needed to check available moves, because there is no move reminder in gen 1,
// There are moves in the level up table that cant be legally obtained
@ -3031,7 +3088,24 @@ namespace PKHeX.Core
}
return slotLocations;
}
private static IEnumerable<DexLevel> getValidPreEvolutions(PKM pkm, int maxspeciesorigin = -1, int lvl = -1, bool skipChecks = false)
internal static int getEncounterLevel(PKM pkm, object encounter)
{
return (encounter as IEncounterable[])?.Min(e => e.LevelMin) ??
(encounter as IEncounterable)?.LevelMin ??
(pkm.GenNumber <= 3 ? 5 : 1 ); //egg
}
internal static int getEncounterSpecies(PKM pkm, DexLevel[] vs, object encounter)
{
if (encounter is int)
return (int)encounter;
if (encounter is IEncounterable[])
return vs.Reverse().First(s => ((IEncounterable[])encounter).Any(slot => slot.Species == s.Species)).Species;
if (encounter is IEncounterable)
return vs.Reverse().First(s => ((IEncounterable)encounter).Species == s.Species).Species;
// encounter is null, is an egg or invalid origin, return base species
return vs.Last().Species;
}
internal static IEnumerable<DexLevel> getValidPreEvolutions(PKM pkm, int maxspeciesorigin = -1, int lvl = -1, bool skipChecks = false)
{
if (lvl < 0)
lvl = pkm.CurrentLevel;
@ -3046,8 +3120,10 @@ namespace PKHeX.Core
new DexLevel { Species = 292, Level = lvl, MinLevel = 20 },
new DexLevel { Species = 290, Level = lvl-1, MinLevel = 1 }
};
if (maxspeciesorigin == -1 && pkm.InhabitedGeneration(2) && pkm.GenNumber == 1)
maxspeciesorigin = MaxSpeciesID_2;
var et = getEvolutionTable(pkm);
var et = maxspeciesorigin == MaxSpeciesID_2 ? getEvolutionTable(2) : getEvolutionTable(pkm);
return et.getValidPreEvolutions(pkm, lvl: lvl, maxSpeciesOrigin: maxspeciesorigin, skipChecks: skipChecks);
}
private static IEnumerable<EncounterStatic> getStatic(PKM pkm, IEnumerable<EncounterStatic> table, int maxspeciesorigin =-1, int lvl = -1)

View file

@ -63,6 +63,11 @@ namespace PKHeX.Core
public static string V372 { get; set; } = "{0} Berry";
public static string V380 { get; set; } = "Encounter Type match encounter.";
public static string V382 { get; set; } = "Encounter Type not implemented for pokemon encounter.";
public static string V391 { get; set; } = "Porygon with valid Type A and B values.";
public static string V392 { get; set; } = "Valid Type A, match species type.";
public static string V393 { get; set; } = "Valid Type B, match species type.";
public static string V394 { get; set; } = "Catch rate match a valid held item from generation 2";
public static string V398 { get; set; } = "Catch Rate match a species from pokemon evolution chain.";
#endregion
#region Legality Check Result Strings
@ -374,6 +379,15 @@ namespace PKHeX.Core
public static string V379 {get; set;} = "{0} Inherited Move. Incompatible with {1} inherited moves.";
public static string V381 {get; set;} = "Encounter Type does not match encounter.";
public static string V385 {get; set;} = "Moves combinations is not compatible with {0} evolution.";
public static string V386 {get; set;} = "Porygon with invalid Type A value.";
public static string V387 {get; set;} = "Porygon with invalid Type B value.";
public static string V388 {get; set;} = "Porygon with invalid Type A and B values. Does not a match a valid type combination.";
public static string V389 {get; set;} = "Invalid Type A, does not match species type.";
public static string V390 {get; set;} = "Invalid Type B, does not match species type.";
public static string V395 {get; set;} = "Catch rate does not match a valid held item from generation 2";
public static string V396 {get; set;} = "Catch rate match species without encounters. Expected a preevolution catch rate.";
public static string V397 {get; set;} = "Catch rate does not match any species from pokemon evolution chain.";
public static string V399 {get; set;} = "Catch rate does not match any species from pokemon evolution chain or any generation 2 held items.";
#endregion
}

View file

@ -0,0 +1,10 @@
namespace PKHeX.Core
{
public enum TradebackType
{
Any,
Gen1_NotTradeback,
Gen2_NotTradeback,
WasTradeback
}
}

View file

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
public class ValidEncounterMoves
{
public int EncounterSpecies { get; set; }
public DexLevel[][] EvolutionChains { get; set; }
public List<int>[] validLevelUpMoves { get; set; }
public List<int>[] validTMHMMoves { get; set; }
public List<int>[] validTutorMoves { get; set; }
public int minLvlG1 { get; set; }
}
}

View file

@ -112,70 +112,63 @@ namespace PKHeX.Core
new EncounterStatic { Species = 054, Level = 15, Moves = new [] { 133, 10 }, Version = GameVersion.Stadium }, // Stadium Psyduck (Amnesia)
new EncounterStatic { Species = 151, Level = 5, IVs = new [] {15,15,15,15,15,15}, Version = GameVersion.VCEvents }, // Event Mew
};
internal static readonly EncounterTrade[] TradeGift_RBY =
{
internal static readonly EncounterTrade[] TradeGift_RBY_Common =
{
// Species & Minimum level (legal) possible to acquire at.
//new EncounterTrade { Species = 122, Generation = 1, Level = 06 }, // Mr. Mime - Game Corner Abra
new EncounterTrade { Species = 032, Generation = 1, Level = 02 }, // Nidoran♂ - Wild Nidoran♀
new EncounterTrade { Species = 029, Generation = 1, Level = 02 }, // Nidoran♀ - Wild Nidoran♂
new EncounterTrade { Species = 030, Generation = 1, Level = 16 }, // Nidorina - Evolve Nidorino
new EncounterTrade { Species = 108, Generation = 1, Level = 15 }, // Lickitung - Surf Slowbro
new EncounterTrade { Species = 124, Generation = 1, Level = 15 }, // Jynx - Fish Poliwhirl (GSC: 10)
new EncounterTrade { Species = 083, Generation = 1, Level = 02 }, // Farfetchd - Wild Spearow
new EncounterTrade { Species = 101, Generation = 1, Level = 03 }, // Electrode - Wild Raichu
new EncounterTrade { Species = 114, Generation = 1, Level = 13 }, // Tangela - Wild Venonat (GSC: 5)
new EncounterTrade { Species = 086, Generation = 1, Level = 28 }, // Seel - Wild Ponyta (GSC: 6)
new EncounterTrade { Species = 122, Generation = 1, Level = 03 }, // Mr. Mime - Wild Jigglypuff
new EncounterTrade { Species = 060, Generation = 1, Level = 02 }, // Poliwag - Wild Rattata
//new EncounterTrade { Species = 083, Generation = 1, Level = 02 }, // Farfetchd - Wild Pidgey
new EncounterTrade { Species = 079, Generation = 1, Level = 22 }, // Slowpoke - Wild Seel
new EncounterTrade { Species = 051, Generation = 1, Level = 15 }, // Dugtrio - Trade Lickitung
new EncounterTrade { Species = 047, Generation = 1, Level = 13 }, // Parasect - Trade Tangela
};
internal static readonly EncounterTrade[] TradeGift_RBY_NoTradeback = TradeGift_RBY_Common.Concat(new[]
{
// Species & Minimum level (legal) possible to acquire at.
new EncounterTrade { Species = 124, Generation = 1, Level = 15 }, // Jynx - Fish Poliwhirl (GSC: 10)
new EncounterTrade { Species = 114, Generation = 1, Level = 13 }, // Tangela - Wild Venonat (GSC: 5)
new EncounterTrade { Species = 086, Generation = 1, Level = 28 }, // Seel - Wild Ponyta (GSC: 6)
new EncounterTrade { Species = 115, Generation = 1, Level = 42 }, // Kangaskhan - Evolve Rhydon (GSC: 30)
new EncounterTrade { Species = 128, Generation = 1, Level = 28 }, // Tauros - Evolve Persian (GSC: 18)
new EncounterTrade { Species = 093, Generation = 1, Level = 20 }, // Haunter - Trade Machoke (GSC: 10)
//new EncounterTrade { Species = 083, Generation = 1, Level = 02 }, // Farfetchd - Wild Pidgey
new EncounterTrade { Species = 075, Generation = 1, Level = 16 }, // Graveler - Evolve Kadabra (GSC: 15)
new EncounterTrade { Species = 079, Generation = 1, Level = 22 }, // Slowpoke - Wild Seel
new EncounterTrade { Species = 098, Generation = 1, Level = 15 }, // Krabby - Wild Growlithe (GSC: 5)
//new EncounterTrade { Species = 122, Generation = 1, Level = 08 }, // Mr. Mime - Wild Clefairy (GSC: 6)
new EncounterTrade { Species = 067, Generation = 1, Level = 20 }, // Machoke - Wild Cubone (GSC: 10)
new EncounterTrade { Species = 051, Generation = 1, Level = 15 }, // Dugtrio - Trade Lickitung
new EncounterTrade { Species = 047, Generation = 1, Level = 13 }, // Parasect - Trade Tangela
new EncounterTrade { Species = 112, Generation = 1, Level = 15 }, // Rhydon - Surf Golduck (GSC: 10)
new EncounterTrade { Species = 087, Generation = 1, Level = 15 }, // Dewgong - Wild Growlithe (GSC: 5)
new EncounterTrade { Species = 089, Generation = 1, Level = 25 }, // Muk - Wild Kangaskhan (GSC: 14)
};
internal static readonly EncounterTrade[] TradeGift_RBY_2 =
}).ToArray();
internal static readonly EncounterTrade[] TradeGift_RBY_Tradeback = TradeGift_RBY_Common.Concat(new[]
{
// Trade gifts that can be obtained at a lower level due to the requested Pokémon being a lower level in GSC
//new EncounterTrade { Species = 122, Generation = 1, Level = 06 }, // Mr. Mime - Game Corner Abra
//new EncounterTrade { Species = 032, Generation = 1, Level = 02 }, // Nidoran♂ - Wild Nidoran♀
//new EncounterTrade { Species = 029, Generation = 1, Level = 02 }, // Nidoran♀ - Wild Nidoran♂
//new EncounterTrade { Species = 030, Generation = 1, Level = 16 }, // Nidorina - Evolve Nidorino
//new EncounterTrade { Species = 108, Generation = 1, Level = 15 }, // Lickitung - Surf Slowbro
new EncounterTrade { Species = 124, Generation = 1, Level = 10 }, // Jynx - Fish Poliwhirl (RBY: 15)
//new EncounterTrade { Species = 083, Generation = 1, Level = 02 }, // Farfetchd - Wild Spearow
//new EncounterTrade { Species = 101, Generation = 1, Level = 03 }, // Electrode - Wild Raichu
new EncounterTrade { Species = 114, Generation = 1, Level = 05 }, // Tangela - Wild Venonat (RBY: 13)
new EncounterTrade { Species = 086, Generation = 1, Level = 05 }, // Seel - Egg Ponyta (RBY: 28)
//new EncounterTrade { Species = 122, Generation = 1, Level = 03 }, // Mr. Mime - Wild Jigglypuff
//new EncounterTrade { Species = 060, Generation = 1, Level = 02 }, // Poliwag - Wild Rattata
new EncounterTrade { Species = 115, Generation = 1, Level = 30 }, // Kangaskhan - Evolve Rhydon (RBY: 42)
new EncounterTrade { Species = 128, Generation = 1, Level = 18 }, // Tauros - Evolve Persian (RBY: 28)
new EncounterTrade { Species = 093, Generation = 1, Level = 10 }, // Haunter - Trade Machoke (RBY: 20)
//new EncounterTrade { Species = 083, Generation = 1, Level = 02 }, // Farfetchd - Wild Pidgey
new EncounterTrade { Species = 075, Generation = 1, Level = 15 }, // Graveler - Evolve Kadabra (RBY: 16)
//new EncounterTrade { Species = 079, Generation = 1, Level = 22 }, // Slowpoke - Wild Seel
new EncounterTrade { Species = 098, Generation = 1, Level = 05 }, // Krabby - Egg Growlithe (RBY: 15)
//new EncounterTrade { Species = 122, Generation = 1, Level = 08 }, // Mr. Mime - Wild Clefairy (RBY: 6)
new EncounterTrade { Species = 067, Generation = 1, Level = 05 }, // Machoke - Egg Cubone (RBY: 20)
//new EncounterTrade { Species = 051, Generation = 1, Level = 15 }, // Dugtrio - Trade Lickitung
//new EncounterTrade { Species = 047, Generation = 1, Level = 13 }, // Parasect - Trade Tangela
new EncounterTrade { Species = 112, Generation = 1, Level = 10 }, // Rhydon - Surf Golduck (RBY: 15)
new EncounterTrade { Species = 087, Generation = 1, Level = 05 }, // Dewgong - Egg Growlithe (RBY: 15)
new EncounterTrade { Species = 089, Generation = 1, Level = 05 }, // Muk - Egg Kangaskhan (RBY: 25)
};
}).ToArray();
internal static readonly EncounterArea FishOldGood_RBY = new EncounterArea { Location = -1, Slots = new EncounterSlot[]
{
new EncounterSlot1 {Species = 129, LevelMin = 05, LevelMax = 05, Type = SlotType.Old_Rod, Rate = -1, }, // Magikarp
@ -197,5 +190,13 @@ namespace PKHeX.Core
{
25, 26, 29, 30, 31, 32, 33, 34, 36, 38, 40, 59, 91, 103, 114, 121,
};
internal static readonly int[] Types_Gen1 =
{
0, 1, 2, 3, 4, 5, 7, 8, 20, 21, 22, 23, 24, 25, 26
};
internal static readonly int[] Species_NotAvailable_CatchRate =
{
12, 18, 31, 34, 36, 38, 45, 53, 59, 62, 65, 68, 71, 76, 78, 91, 94, 103, 121
};
}
}

View file

@ -175,6 +175,8 @@
<Compile Include="Legality\Structures\Nature.cs" />
<Compile Include="Legality\Structures\PIDIV.cs" />
<Compile Include="Legality\Structures\SlotType.cs" />
<Compile Include="Legality\Structures\TradebackType.cs" />
<Compile Include="Legality\Structures\ValidEncounterMoves.cs" />
<Compile Include="Legality\Tables.cs" />
<Compile Include="Legality\Tables2.cs" />
<Compile Include="Legality\Tables1.cs" />

View file

@ -141,12 +141,11 @@ namespace PKHeX.Core
get { return PKX.getG1Species(Data[0]); }
set
{
int currentRate = PersonalInfo.CatchRate;
Data[0] = (byte)PKX.setG1Species(value);
// Before updating catch rate, check if non-standard
if (Catch_Rate == currentRate)
Catch_Rate = PersonalInfo.CatchRate;
if (!CatchRateIsItem)
Catch_Rate = PersonalTable.RB[Legal.getBaseSpecies(this)].CatchRate;
Type_A = PersonalInfo.Types[0];
Type_B = PersonalInfo.Types[1];
}
@ -273,6 +272,7 @@ namespace PKHeX.Core
public override int CNT_Tough { get { return 0; } set { } }
public override int CNT_Sheen { get { return 0; } set { } }
#endregion
public bool CatchRateIsItem = false;
public PK2 convertToPK2()
{

View file

@ -310,7 +310,8 @@ namespace PKHeX.Core
if (Gen3) return 3;
if (Gen2) return Format; // 2
if (Gen1) return Format; // 1
if (VC) return 1;
if (VC1) return 1;
if (VC2) return 2;
return -1;
}
}
@ -435,6 +436,9 @@ namespace PKHeX.Core
}
// Legality Extensions
public TradebackType TradebackStatus { get; set; } = TradebackType.Any;
public bool Gen1_NotTradeback => TradebackStatus == TradebackType.Gen1_NotTradeback;
public bool Gen2_NotTradeback => TradebackStatus == TradebackType.Gen2_NotTradeback;
public virtual bool WasLink => false;
private bool _WasEgg;
public virtual bool WasEgg
@ -517,9 +521,6 @@ namespace PKHeX.Core
if (Format == Generation)
return true;
if (Format < Generation)
return false; // Future
if (!IsOriginValid)
return false;
@ -527,11 +528,22 @@ namespace PKHeX.Core
if (Legal.getMaxSpeciesOrigin(GenNumber) < species && !Legal.getFutureGenEvolutions(GenNumber).Contains(species))
return false;
// Trade generation 1 -> 2
if (Format == 2 && Generation == 1 && !Gen2_NotTradeback)
return true;
// Trade generation 2 -> 1
if (Format == 1 && Generation == 2 && !Gen1_NotTradeback)
return true;
if (Format < Generation)
return false; // Future
int gen = GenNumber;
switch (Generation)
{
case 1:
case 2: return Format <= 2 || VC;
case 1: return Format == 1 || VC1;
case 2: return Format == 2 || VC2;
case 3: return Gen3;
case 4: return 3 <= gen && gen <= 4;
case 5: return 3 <= gen && gen <= 5;

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
@ -180,5 +181,9 @@ namespace PKHeX.Core
}
return result;
}
public bool IsValidTypeCombination(int Type1, int Type2)
{
return Table.Any(p => p.Types[0] == Type1 && p.Types[1] == Type2);
}
}
}

View file

@ -35,6 +35,11 @@ V362 = Default move in generation {0}.
V372 = {0} Berry
V380 = Encounter Type match encounter.
V382 = Encounter Type not implemented for pokemon encounter.
V391 = Porygon with valid Type A and B values.
V392 = Valid Type A, match species type.
V393 = Valid Type B, match species type.
V394 = Catch rate match a valid held item from generation 2.
V398 = Catch Rate match any species from pokemon evolution chain.
V203 = Genderless Pokémon should not have a gender.
V201 = Encryption Constant is not set.
V204 = Held item is unreleased.
@ -315,4 +320,13 @@ V377 = Egg Move. Not expected in a gift egg.
V378 = Inherited move learned by Level-up. Not expected in a gift egg.
V379 = {0} Inherited Move. Incompatible with {1} inherited moves.
V381 = Encounter Type does not match encounter.
V385 = Moves combinations is not compatible with {0} evolution.
V385 = Moves combinations is not compatible with {0} evolution.
V386 = Porygon with invalid Type A value.
V387 = Porygon with invalid Type B value.
V388 = Porygon with invalid Type A and B values. Does not a match a valid type combination.
V389 = Invalid Type A, does not match species type.
V390 = Invalid Type B, does not match species type.
V395 = Catch rate does not match a valid held item from generation 2.
V396 = Catch rate match species without encounters. Expected a preevolution catch rate.
V397 = Catch rate does not match any species from pokemon evolution chain.
V399 = Catch rate does not match any species from pokemon evolution chain or any generation 2 held items.

View file

@ -35,6 +35,12 @@ V362 = {0}세대 기본 기술입니다.
V372 = {0}열매
V380 = Encounter Type match encounter.
V382 = Encounter Type not implemented for pokemon encounter.
V382 = Encounter Type not implemented for pokemon encounter.
V391 = Porygon with valid Type A and B values.
V392 = Valid Type A, match species type.
V393 = Valid Type B, match species type.
V394 = Catch rate match a valid held item from generation 2.
V398 = Catch rate match any species from pokemon evolution chain.
V203 = 무성 포켓몬은 성별을 가질 수 없습니다.
V201 = 암호화 상수가 설정되어 있지 않습니다.
V204 = 지닌 물건이 배포되지 않은 물건입니다.
@ -314,4 +320,13 @@ V377 = 자력기입니다. 이벤트 알에서 예상된 기술이 아닙니다.
V378 = 유전받은 레벨업 기술입니다. 이벤트 알에서 예상된 기술이 아닙니다.
V379 = 유전받은 {0} 기술입니다. 유전받은 {1} 기술과 함께 존재할 수 없습니다.
V381 = Encounter Type does not match encounter.
V385 = Moves combinations is not compatible with {0} evolution.
V385 = Moves combinations is not compatible with {0} evolution.
V386 = Porygon with invalid Type A value.
V387 = Porygon with invalid Type B value.
V388 = Porygon with invalid Type A and B values. Does not a match a valid type combination.
V389 = Invalid Type A, does not match species type.
V390 = Invalid Type B, does not match species type.
V395 = Catch rate does not match a valid held item from generation 2.
V396 = Catch rate match species without encounters. Expected a preevolution catch rate.
V397 = Catch rate does not match any species from pokemon evolution chain.
V399 = Catch rate does not match any species from pokemon evolution chain or any generation 2 held items.

View file

@ -35,6 +35,12 @@ V362 = 在第{0}世代的默认招式。
V372 = {0}树果
V380 = Encounter Type match encounter.
V382 = Encounter Type not implemented for pokemon encounter.
V382 = Encounter Type not implemented for pokemon encounter.
V391 = Porygon with valid Type A and B values.
V392 = Valid Type A, match species type.
V393 = Valid Type B, match species type.
V394 = Catch rate match a valid held item from generation 2
V398 = Catch rate match any species from pokemon evolution chain.
V203 = 无性别宝可梦不能有性别。
V201 = 未设置加密常数。
V204 = 持有物未解禁。
@ -314,4 +320,13 @@ V377 = 遗传招式。礼物蛋不应有。
V378 = 遗传升级招式。礼物蛋不应有。
V379 = {0}的遗传招式。与遗传的招式{1}不共存。
V381 = Encounter Type does not match encounter.
V385 = Moves combinations is not compatible with {0} evolution.
V385 = Moves combinations is not compatible with {0} evolution.
V386 = Porygon with invalid Type A value.
V387 = Porygon with invalid Type B value.
V388 = Porygon with invalid Type A and B values. Does not a match a valid type combination.
V389 = Invalid Type A, does not match species type.
V390 = Invalid Type B, does not match species type.
V395 = Catch rate does not match a valid held item from generation 2.
V396 = Catch rate match species without encounters. Expected a preevolution catch rate.
V397 = Catch rate does not match any species from pokemon evolution chain.
V399 = Catch rate does not match any species from pokemon evolution chain or any generation 2 held items.