mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-21 17:48:28 -05:00
HOME 2.0.0: Handle conversion behavior & restrictions (#3506)
* Revises legality checks to account for traveling between the three game islands (PLA/BDSP/SWSH) * Adds conversion mechanisms between the three formats, as well as flexible conversion options to backfill missing data (thanks GameFreak/ILCA for opting for lossy conversion instead of updating the games). * Adds API abstractions for HOME data storage format (EKH/PKH format 1, aka EH1/PH1). * Revises some APIs for better usage: - `PKM` now exposes a `Context` to indicate the isolation context for legality purposes. - Some method signatures have changed to accept `Context` or `GameVersion` instead of a vague `int` for Generation. - Evolution History is now tracked in the Legality parse for specific contexts, rather than only per generation.
This commit is contained in:
parent
5ae34854c7
commit
5bcccc6d92
|
|
@ -43,7 +43,7 @@ public static class BatchMods
|
|||
new ComplexSuggestion(PROP_RIBBONS, (_, value, info) => BatchModifications.SetSuggestedRibbons(info, value)),
|
||||
new ComplexSuggestion(nameof(PKM.Met_Location), (_, _, info) => BatchModifications.SetSuggestedMetData(info)),
|
||||
new ComplexSuggestion(nameof(PKM.CurrentLevel), (_, _, info) => BatchModifications.SetMinimumCurrentLevel(info)),
|
||||
new ComplexSuggestion(PROP_CONTESTSTATS, p => p is IContestStatsMutable, (_, value, info) => BatchModifications.SetContestStats(info.Entity, info.Legality.EncounterMatch, value)),
|
||||
new ComplexSuggestion(PROP_CONTESTSTATS, p => p is IContestStatsMutable, (_, value, info) => BatchModifications.SetContestStats(info.Entity, info.Legality, value)),
|
||||
new ComplexSuggestion(PROP_MOVEMASTERY, (_, value, info) => BatchModifications.SetSuggestedMasteryData(info, value)),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -105,14 +105,14 @@ public static ModifyResult SetEVs(PKM pk)
|
|||
/// Sets the contests stats as requested.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="enc">Encounter matched to.</param>
|
||||
/// <param name="la">Legality Information matched to.</param>
|
||||
/// <param name="option">Option to apply with</param>
|
||||
public static ModifyResult SetContestStats(PKM pk, IEncounterTemplate enc, string option)
|
||||
public static ModifyResult SetContestStats(PKM pk, LegalityAnalysis la, string option)
|
||||
{
|
||||
if (option.Length != 0 && option[BatchEditing.CONST_SUGGEST.Length..] is not "0")
|
||||
pk.SetMaxContestStats(enc);
|
||||
pk.SetMaxContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
|
||||
else
|
||||
pk.SetSuggestedContestStats(enc);
|
||||
pk.SetSuggestedContestStats(la.EncounterMatch, la.Info.EvoChainsAllGens);
|
||||
return ModifyResult.Modified;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
PKHeX.Core/Editing/LocationEdits.cs
Normal file
10
PKHeX.Core/Editing/LocationEdits.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public static class LocationEdits
|
||||
{
|
||||
public static int GetNoneLocation(PKM pk) => pk switch
|
||||
{
|
||||
PB8 => Locations.Default8bNone,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
|
@ -17,7 +17,22 @@ public static class Pokerus
|
|||
/// </summary>
|
||||
/// <param name="pk">Entity to check</param>
|
||||
/// <returns>True if Pokérus exists in the game format, or can be transmitted to the entity via another game.</returns>
|
||||
public static bool IsObtainable(PKM pk) => pk is not PA8; // don't care about PK1, not stored in the data.
|
||||
public static bool IsObtainable(PKM pk) => pk switch
|
||||
{
|
||||
PA8 pa8 => HasVisitedBDSPorSWSH(pa8),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
private static bool HasVisitedBDSPorSWSH(PA8 pk)
|
||||
{
|
||||
if (pk.IsUntraded)
|
||||
return false;
|
||||
if (PersonalTable.BDSP.IsPresentInGame(pk.Species, pk.Form))
|
||||
return true;
|
||||
if (PersonalTable.SWSH.IsPresentInGame(pk.Species, pk.Form))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Pokérus value for Strain is possible to have on the input entity.
|
||||
|
|
|
|||
|
|
@ -91,9 +91,10 @@ public void ReadTemplateIfNoEntity(string path)
|
|||
#region Utility
|
||||
private static SaveFile GetBlank(PKM pk)
|
||||
{
|
||||
var ver = (GameVersion)pk.Version;
|
||||
if (ver.GetGeneration() != pk.Format)
|
||||
ver = GameUtil.GetVersion(pk.Format);
|
||||
var ctx = pk.Context;
|
||||
var ver = ctx.GetSingleGameVersion();
|
||||
if (pk is { Format: 1, Japanese: true })
|
||||
ver = GameVersion.BU;
|
||||
|
||||
return SaveUtil.GetBlankSAV(ver, pk.OT_Name, (LanguageID)pk.Language);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ protected BoxManipBase(BoxManipType type, Func<SaveFile, bool> usable)
|
|||
new BoxManipSort(BoxManipType.SortLevelReverse, EntitySorting.OrderByDescendingLevel),
|
||||
new BoxManipSort(BoxManipType.SortDate, EntitySorting.OrderByDateObtained, s => s.Generation >= 4),
|
||||
new BoxManipSort(BoxManipType.SortName, list => list.OrderBySpeciesName(GameInfo.Strings.Species)),
|
||||
new BoxManipSort(BoxManipType.SortFavorite, list => list.OrderByCustom(pk => pk is PB7 {Favorite: true}), s => s is SAV7b),
|
||||
new BoxManipSort(BoxManipType.SortFavorite, list => list.OrderByCustom(pk => pk is PB7 {Favorite: true}), s => s.BlankPKM is IFavorite),
|
||||
new BoxManipSortComplex(BoxManipType.SortParty, (list, sav, start) => list.BubbleUp(sav, i => ((SAV7b)sav).Blocks.Storage.IsParty(i), start), s => s is SAV7b),
|
||||
new BoxManipSort(BoxManipType.SortShiny, list => list.OrderByCustom(pk => !pk.IsShiny)),
|
||||
new BoxManipSort(BoxManipType.SortRandom, list => list.OrderByCustom(_ => Util.Rand32())),
|
||||
|
|
|
|||
|
|
@ -62,12 +62,12 @@ public enum GameVersion
|
|||
Pt = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Heart Gold (NDS)
|
||||
/// Pokémon HeartGold (NDS)
|
||||
/// </summary>
|
||||
HG = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Soul Silver (NDS)
|
||||
/// Pokémon SoulSilver (NDS)
|
||||
/// </summary>
|
||||
SS = 8,
|
||||
#endregion
|
||||
|
|
@ -139,7 +139,7 @@ public enum GameVersion
|
|||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon GO (GO -> Lets Go transfers)
|
||||
/// Pokémon GO (GO -> Let's Go/HOME transfers)
|
||||
/// </summary>
|
||||
GO = 34,
|
||||
|
||||
|
|
@ -184,12 +184,12 @@ public enum GameVersion
|
|||
|
||||
#region Nintendo Switch
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go Pikachu (NX)
|
||||
/// Pokémon: Let's Go, Pikachu! (NX)
|
||||
/// </summary>
|
||||
GP = 42,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Let's Go Eevee (NX)
|
||||
/// Pokémon: Let's Go, Eevee! (NX)
|
||||
/// </summary>
|
||||
GE = 43,
|
||||
|
||||
|
|
@ -204,6 +204,10 @@ public enum GameVersion
|
|||
SH = 45,
|
||||
|
||||
// HOME = 46,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Legends: Arceus (NX)
|
||||
/// </summary>
|
||||
PLA = 47,
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -311,7 +315,7 @@ public enum GameVersion
|
|||
DPPt,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Heart Gold & Soul Silver [<see cref="SAV4"/>] identifier.
|
||||
/// Pokémon HeartGold & SoulSilver [<see cref="SAV4"/>] identifier.
|
||||
/// </summary>
|
||||
/// <see cref="HG"/>
|
||||
/// <see cref="SS"/>
|
||||
|
|
|
|||
|
|
@ -151,6 +151,13 @@ private static List<ComboItem> CreateGen8(GameStrings s)
|
|||
Util.AddCBWithOffset(locations, s.metSWSH_30000, 30000, Locations8.Met3);
|
||||
Util.AddCBWithOffset(locations, s.metSWSH_40000, 40000, Locations8.Met4);
|
||||
Util.AddCBWithOffset(locations, s.metSWSH_60000, 60000, Locations8.Met6);
|
||||
|
||||
// Add in the BDSP+PLA magic met locations.
|
||||
locations.Add(new ComboItem($"{s.EggName} (BD/SP)", Locations.HOME_SWSHBDSPEgg));
|
||||
locations.Add(new ComboItem(s.gamelist[(int)BD], Locations.HOME_SWBD));
|
||||
locations.Add(new ComboItem(s.gamelist[(int)SP], Locations.HOME_SHSP));
|
||||
locations.Add(new ComboItem(s.gamelist[(int)PLA], Locations.HOME_SWLA));
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
|
|
@ -162,13 +169,20 @@ private static List<ComboItem> CreateGen8a(GameStrings s)
|
|||
Util.AddCBWithOffset(locations, s.metLA_30000, 30000, Locations8a.Met3);
|
||||
Util.AddCBWithOffset(locations, s.metLA_40000, 40000, Locations8a.Met4);
|
||||
Util.AddCBWithOffset(locations, s.metLA_60000, 60000, Locations8a.Met6);
|
||||
|
||||
// Add in the BDSP+PLA magic met locations.
|
||||
locations.Add(new ComboItem($"{s.EggName} (BD/SP)", Locations.HOME_SWSHBDSPEgg));
|
||||
locations.Add(new ComboItem(s.gamelist[(int)BD], Locations.HOME_SWBD));
|
||||
locations.Add(new ComboItem(s.gamelist[(int)SP], Locations.HOME_SHSP));
|
||||
locations.Add(new ComboItem(s.gamelist[(int)PLA], Locations.HOME_SWLA));
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
private static List<ComboItem> CreateGen8b(GameStrings s)
|
||||
{
|
||||
// Manually add invalid (-1) location from SWSH as ID 65535
|
||||
var locations = new List<ComboItem> { new(s.metSWSH_00000[0], unchecked((ushort)Locations.Default8bNone)) };
|
||||
var locations = new List<ComboItem> { new(s.metSWSH_00000[0], Locations.Default8bNone) };
|
||||
Util.AddCBWithOffset(locations, s.metBDSP_60000, 60000, Locations.Daycare5);
|
||||
Util.AddCBWithOffset(locations, s.metBDSP_30000, 30000, Locations.LinkTrade6);
|
||||
Util.AddCBWithOffset(locations, s.metBDSP_00000, 00000, Locations8b.Met0);
|
||||
|
|
@ -193,29 +207,45 @@ public IReadOnlyList<ComboItem> GetLocationList(GameVersion version, int current
|
|||
if (egg && version < W && currentGen >= 5)
|
||||
return MetGen4;
|
||||
|
||||
var result = GetLocationListInternal(version, currentGen);
|
||||
|
||||
// Insert the BDSP none location if the format requires it.
|
||||
if (currentGen == 8 && !BDSP.Contains(version))
|
||||
{
|
||||
var list = new List<ComboItem>(result.Count + 1);
|
||||
list.AddRange(result);
|
||||
list.Insert(1, new ComboItem($"{list[0].Text} (BD/SP)", Locations.Default8bNone));
|
||||
result = list;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IReadOnlyList<ComboItem> GetLocationListInternal(GameVersion version, int currentGen)
|
||||
{
|
||||
return version switch
|
||||
{
|
||||
CXD when currentGen == 3 => MetGen3CXD,
|
||||
R or S when currentGen == 3 => Partition1(MetGen3, z => z <= 87), // Ferry
|
||||
E when currentGen == 3 => Partition1(MetGen3, z => z is <= 87 or >= 197 and <= 212), // Trainer Hill
|
||||
CXD when currentGen == 3 => MetGen3CXD,
|
||||
R or S when currentGen == 3 => Partition1(MetGen3, z => z <= 87), // Ferry
|
||||
E when currentGen == 3 => Partition1(MetGen3, z => z is <= 87 or >= 197 and <= 212), // Trainer Hill
|
||||
FR or LG when currentGen == 3 => Partition1(MetGen3, z => z is > 87 and < 197), // Celadon Dept.
|
||||
D or P when currentGen == 4 => Partition2(MetGen4, z => z <= 111, 4), // Battle Park
|
||||
Pt when currentGen == 4 => Partition2(MetGen4, z => z <= 125, 4), // Rock Peak Ruins
|
||||
D or P when currentGen == 4 => Partition2(MetGen4, z => z <= 111, 4), // Battle Park
|
||||
Pt when currentGen == 4 => Partition2(MetGen4, z => z <= 125, 4), // Rock Peak Ruins
|
||||
HG or SS when currentGen == 4 => Partition2(MetGen4, z => z is > 125 and < 234, 4), // Celadon Dept.
|
||||
|
||||
B or W => MetGen5,
|
||||
B2 or W2 => Partition2(MetGen5, z => z <= 116), // Abyssal Ruins
|
||||
X or Y => Partition2(MetGen6, z => z <= 168), // Unknown Dungeon
|
||||
B or W => MetGen5,
|
||||
B2 or W2 => Partition2(MetGen5, z => z <= 116), // Abyssal Ruins
|
||||
X or Y => Partition2(MetGen6, z => z <= 168), // Unknown Dungeon
|
||||
OR or AS => Partition2(MetGen6, z => z is > 168 and <= 354), // Secret Base
|
||||
SN or MN => Partition2(MetGen7, z => z < 200), // Outer Cape
|
||||
SN or MN => Partition2(MetGen7, z => z < 200), // Outer Cape
|
||||
US or UM
|
||||
or RD or BU or GN or YW
|
||||
or GD or SI or C => Partition2(MetGen7, z => z < 234), // Dividing Peak Tunnel
|
||||
or RD or BU or GN or YW
|
||||
or GD or SI or C => Partition2(MetGen7, z => z < 234), // Dividing Peak Tunnel
|
||||
GP or GE or GO => Partition2(MetGen7GG, z => z <= 54), // Pokémon League
|
||||
SW or SH => Partition2(MetGen8, z => z < 400),
|
||||
BD or SP => Partition2(MetGen8b, z => z < 628),
|
||||
PLA => Partition2(MetGen8a, z => z < 512),
|
||||
_ => GetLocationListModified(version, currentGen),
|
||||
_ => new List<ComboItem>(GetLocationListModified(version, currentGen)),
|
||||
};
|
||||
|
||||
static IReadOnlyList<ComboItem> Partition1(IReadOnlyList<ComboItem> list, Func<int, bool> criteria)
|
||||
|
|
@ -224,7 +254,8 @@ static IReadOnlyList<ComboItem> Partition1(IReadOnlyList<ComboItem> list, Func<i
|
|||
return GetOrderedList(list, result, criteria);
|
||||
}
|
||||
|
||||
static IReadOnlyList<ComboItem> GetOrderedList(IReadOnlyList<ComboItem> list, ComboItem[] result, Func<int, bool> criteria, int start = 0)
|
||||
static IReadOnlyList<ComboItem> GetOrderedList(IReadOnlyList<ComboItem> list, ComboItem[] result,
|
||||
Func<int, bool> criteria, int start = 0)
|
||||
{
|
||||
// store values that match criteria at the next available position of the array
|
||||
// store non-matches starting at the end. reverse before returning
|
||||
|
|
@ -237,12 +268,14 @@ static IReadOnlyList<ComboItem> GetOrderedList(IReadOnlyList<ComboItem> list, Co
|
|||
else
|
||||
result[end--] = item;
|
||||
}
|
||||
|
||||
// since the non-matches are reversed in order, we swap them back since we know where they end up at.
|
||||
Array.Reverse(result, start, list.Count - start);
|
||||
return result;
|
||||
}
|
||||
|
||||
static IReadOnlyList<ComboItem> Partition2(IReadOnlyList<ComboItem> list, Func<int, bool> criteria, int keepFirst = 3)
|
||||
static IReadOnlyList<ComboItem> Partition2(IReadOnlyList<ComboItem> list, Func<int, bool> criteria,
|
||||
int keepFirst = 3)
|
||||
{
|
||||
var result = new ComboItem[list.Count];
|
||||
for (int i = 0; i < keepFirst; i++)
|
||||
|
|
|
|||
|
|
@ -100,10 +100,34 @@ public static class Locations
|
|||
|
||||
public const int BugCatchingContest4 = 207;
|
||||
|
||||
/// <summary>
|
||||
/// -1
|
||||
/// </summary>
|
||||
public const int Default8bNone = -1;
|
||||
public const int HOME_SHSP = 59998; // SP traded to (SW)SH
|
||||
public const int HOME_SWBD = 59999; // BD traded to SW(SH)
|
||||
public const int HOME_SWLA = 60000; // PLA traded to SW(SH)
|
||||
public const int HOME_SWSHBDSPEgg = 65534; // -2 = 8bNone-1..
|
||||
public const int Default8bNone = 65535;
|
||||
|
||||
public static int GetVersionSWSH(int ver) => (GameVersion)ver switch
|
||||
{
|
||||
GameVersion.PLA => (int)GameVersion.SW,
|
||||
GameVersion.BD => (int)GameVersion.SW,
|
||||
GameVersion.SP => (int)GameVersion.SH,
|
||||
_ => ver,
|
||||
};
|
||||
|
||||
public static int GetMetSWSH(int loc, int ver) => (GameVersion)ver switch
|
||||
{
|
||||
GameVersion.PLA => HOME_SWLA,
|
||||
GameVersion.BD => HOME_SWBD,
|
||||
GameVersion.SP => HOME_SHSP,
|
||||
_ => loc,
|
||||
};
|
||||
|
||||
public static bool IsValidMetBDSP(int loc, int ver) => loc switch
|
||||
{
|
||||
HOME_SHSP when ver == (int)GameVersion.SH => true,
|
||||
HOME_SWBD when ver == (int)GameVersion.SW => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public static int TradedEggLocationNPC(int generation) => generation switch
|
||||
{
|
||||
|
|
@ -125,6 +149,7 @@ public static class Locations
|
|||
|
||||
public static bool IsPtHGSSLocation(int location) => location is > 111 and < 2000;
|
||||
public static bool IsPtHGSSLocationEgg(int location) => location is > 2010 and < 3000;
|
||||
public static bool IsEventLocation3(int location) => location is 255;
|
||||
public static bool IsEventLocation4(int location) => location is >= 3000 and <= 3076;
|
||||
public static bool IsEventLocation5(int location) => location is > 40000 and < 50000;
|
||||
|
||||
|
|
@ -146,14 +171,6 @@ public static bool IsEggLocationBred4(int loc, GameVersion ver)
|
|||
public static bool IsEggLocationBred6(int loc) => loc is Daycare5 or LinkTrade6;
|
||||
public static bool IsEggLocationBred8b(int loc) => loc is Daycare8b or LinkTrade6NPC;
|
||||
|
||||
public static bool IsNoneLocation(GameVersion ver, int location) => GetNoneLocation(ver) == (short)location;
|
||||
|
||||
public static int GetNoneLocation(GameVersion ver) => ver switch
|
||||
{
|
||||
GameVersion.BD or GameVersion.SP => Default8bNone,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public static int GetDaycareLocation(int generation, GameVersion version) => generation switch
|
||||
{
|
||||
1 or 2 or 3 => 0,
|
||||
|
|
|
|||
|
|
@ -35,19 +35,21 @@ internal static EncounterArea7g[] GetArea(BinLinkerAccessor data)
|
|||
return areas;
|
||||
}
|
||||
|
||||
private const int meta = 4;
|
||||
private const int entrySize = (2 * sizeof(int)) + 2;
|
||||
|
||||
private static EncounterArea7g GetArea(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var species = ReadUInt16LittleEndian(data);
|
||||
byte form = (byte)(species >> 11);
|
||||
species &= 0x3FF;
|
||||
var form = data[2];
|
||||
//var import = (EntityFormatDetected)data[3];
|
||||
|
||||
var result = new EncounterSlot7GO[(data.Length - 2) / entrySize];
|
||||
data = data[meta..];
|
||||
var result = new EncounterSlot7GO[data.Length / entrySize];
|
||||
var area = new EncounterArea7g(species, form, result);
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var offset = (i * entrySize) + 2;
|
||||
var offset = i * entrySize;
|
||||
var entry = data.Slice(offset, entrySize);
|
||||
result[i] = ReadSlot(entry, area, species, form);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,29 +35,29 @@ internal static EncounterArea8g[] GetArea(BinLinkerAccessor data)
|
|||
return areas;
|
||||
}
|
||||
|
||||
private const int meta = 4;
|
||||
private const int entrySize = (2 * sizeof(int)) + 2;
|
||||
|
||||
private static EncounterArea8g GetArea(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var species = ReadUInt16LittleEndian(data);
|
||||
byte form = (byte)(species >> 11);
|
||||
species &= 0x3FF;
|
||||
var form = data[2];
|
||||
var import = (PogoImportFormat)data[3];
|
||||
|
||||
var group = GetGroup(species, form);
|
||||
|
||||
var result = new EncounterSlot8GO[(data.Length - 2) / entrySize];
|
||||
data = data[meta..];
|
||||
var result = new EncounterSlot8GO[data.Length / entrySize];
|
||||
var area = new EncounterArea8g(species, form, result);
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
var offset = (i * entrySize) + 2;
|
||||
var offset = i * entrySize;
|
||||
var entry = data.Slice(offset, entrySize);
|
||||
result[i] = ReadSlot(entry, area, species, form, group);
|
||||
result[i] = ReadSlot(entry, area, species, form, import);
|
||||
}
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
private static EncounterSlot8GO ReadSlot(ReadOnlySpan<byte> entry, EncounterArea8g area, ushort species, byte form, GameVersion group)
|
||||
private static EncounterSlot8GO ReadSlot(ReadOnlySpan<byte> entry, EncounterArea8g area, ushort species, byte form, PogoImportFormat format)
|
||||
{
|
||||
int start = ReadInt32LittleEndian(entry);
|
||||
int end = ReadInt32LittleEndian(entry[4..]);
|
||||
|
|
@ -65,32 +65,7 @@ private static EncounterSlot8GO ReadSlot(ReadOnlySpan<byte> entry, EncounterArea
|
|||
var shiny = (Shiny)(sg & 0x3F);
|
||||
var gender = (Gender)(sg >> 6);
|
||||
var type = (PogoType)entry[9];
|
||||
return new EncounterSlot8GO(area, species, form, start, end, shiny, gender, type, group);
|
||||
}
|
||||
|
||||
private static GameVersion GetGroup(int species, int form)
|
||||
{
|
||||
// Transfer Rules:
|
||||
// If it can exist in LGP/E, it uses LGP/E's move data for the initial moves.
|
||||
// Else, if it can exist in SW/SH, it uses SW/SH's move data for the initial moves.
|
||||
// Else, it must exist in US/UM, thus it uses US/UM's moves.
|
||||
|
||||
var pt8 = PersonalTable.SWSH;
|
||||
var ptGG = PersonalTable.GG;
|
||||
|
||||
var pi8 = (PersonalInfoSWSH)pt8[species];
|
||||
if (pi8.IsPresentInGame)
|
||||
{
|
||||
bool lgpe = (species is (<= 151 or 808 or 809)) && (form == 0 || ptGG[species].HasForm(form));
|
||||
return lgpe ? GameVersion.GG : GameVersion.SWSH;
|
||||
}
|
||||
if (species <= Legal.MaxSpeciesID_7_USUM)
|
||||
{
|
||||
bool lgpe = species <= 151 && (form == 0 || ptGG[species].HasForm(form));
|
||||
return lgpe ? GameVersion.GG : GameVersion.USUM;
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(species));
|
||||
return new EncounterSlot8GO(area, species, form, start, end, shiny, gender, type, format);
|
||||
}
|
||||
|
||||
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, EvoCriteria[] chain)
|
||||
|
|
@ -101,7 +76,7 @@ public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, EvoCriteria
|
|||
// Find the first chain that has slots defined.
|
||||
// Since it is possible to evolve before transferring, we only need the highest evolution species possible.
|
||||
// PoGoEncTool has already extrapolated the evolutions to separate encounters!
|
||||
var sf = Array.Find(chain, z => z.Species == Species && (z.Form == Form || FormInfo.IsFormChangeable(Species, Form, z.Form, pkm.Format)));
|
||||
var sf = FindCriteriaToIterate(pkm, chain);
|
||||
if (sf == default)
|
||||
yield break;
|
||||
|
||||
|
|
@ -133,5 +108,25 @@ public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, EvoCriteria
|
|||
if (deferredIV != null)
|
||||
yield return deferredIV;
|
||||
}
|
||||
|
||||
private EvoCriteria FindCriteriaToIterate(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
foreach (var evo in chain)
|
||||
{
|
||||
if (evo.Species != Species)
|
||||
continue;
|
||||
|
||||
if (evo.Form == Form)
|
||||
return evo;
|
||||
|
||||
// Check for form mismatches
|
||||
if (FormInfo.IsFormChangeable(Species, Form, evo.Form, pkm.Format))
|
||||
return evo;
|
||||
if (Species == (int)Core.Species.Burmy)
|
||||
return evo;
|
||||
break;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,8 +141,11 @@ public static bool CanHatchAsEgg(int species, int form, GameVersion game)
|
|||
{
|
||||
// Sanity check form for origin
|
||||
var pt = GameData.GetPersonal(game);
|
||||
if ((uint)species > pt.MaxSpeciesID)
|
||||
return false;
|
||||
|
||||
var entry = pt.GetFormEntry(species, form);
|
||||
if (entry is PersonalInfoSWSH { IsPresentInGame: false })
|
||||
if (!entry.IsPresentInGame)
|
||||
return false;
|
||||
return form < entry.FormCount || (species == (int)Rotom && form <= 5);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,23 @@ internal static int GetMaxSpeciesOrigin(PKM pkm)
|
|||
_ => -1,
|
||||
};
|
||||
|
||||
internal static int GetMaxSpeciesOrigin(EntityContext context) => context switch
|
||||
{
|
||||
EntityContext.Gen1 => MaxSpeciesID_1,
|
||||
EntityContext.Gen2 => MaxSpeciesID_2,
|
||||
EntityContext.Gen3 => MaxSpeciesID_3,
|
||||
EntityContext.Gen4 => MaxSpeciesID_4,
|
||||
EntityContext.Gen5 => MaxSpeciesID_5,
|
||||
EntityContext.Gen6 => MaxSpeciesID_6,
|
||||
EntityContext.Gen7 => MaxSpeciesID_7_USUM,
|
||||
EntityContext.Gen8 => MaxSpeciesID_8_R2,
|
||||
|
||||
EntityContext.Gen7b => MaxSpeciesID_7b,
|
||||
EntityContext.Gen8a => MaxSpeciesID_8a,
|
||||
EntityContext.Gen8b => MaxSpeciesID_8b,
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
internal static int GetMaxSpeciesOrigin(int generation) => generation switch
|
||||
{
|
||||
1 => MaxSpeciesID_1,
|
||||
|
|
@ -169,43 +186,93 @@ internal static int GetMaxSpeciesOrigin(PKM pkm)
|
|||
internal static bool HasVisitedORAS(this PKM pkm, int species) => pkm.InhabitedGeneration(6, species) && (pkm.AO || !pkm.IsUntraded);
|
||||
internal static bool HasVisitedUSUM(this PKM pkm, int species) => pkm.InhabitedGeneration(7, species) && (pkm.USUM || !pkm.IsUntraded);
|
||||
|
||||
internal static bool HasVisitedBDSP(this PKM pkm, int species)
|
||||
internal static bool HasVisitedSWSH(this PKM pkm, EvoCriteria[] evos)
|
||||
{
|
||||
if (!pkm.InhabitedGeneration(8, species))
|
||||
if (pkm.SWSH)
|
||||
return true;
|
||||
if (pkm.IsUntraded)
|
||||
return false;
|
||||
if (pkm.BDSP && pkm.Species is (int)Species.Spinda or (int)Species.Nincada)
|
||||
return false;
|
||||
|
||||
var pt = PersonalTable.SWSH;
|
||||
foreach (var evo in evos)
|
||||
{
|
||||
if (pt.IsPresentInGame(evo.Species, evo.Form))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool HasVisitedBDSP(this PKM pkm, EvoCriteria[] evos)
|
||||
{
|
||||
if (pkm.BDSP)
|
||||
return true;
|
||||
if (pkm.IsUntraded)
|
||||
return false;
|
||||
var pi = (PersonalInfoBDSP)PersonalTable.BDSP[species];
|
||||
return pi.IsPresentInGame;
|
||||
if (pkm.Species is (int)Species.Spinda or (int)Species.Nincada)
|
||||
return false;
|
||||
|
||||
var pt = PersonalTable.BDSP;
|
||||
foreach (var evo in evos)
|
||||
{
|
||||
if (pt.IsPresentInGame(evo.Species, evo.Form))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool HasVisitedLA(this PKM pkm, int species)
|
||||
internal static bool HasVisitedLA(this PKM pkm, EvoCriteria[] evos)
|
||||
{
|
||||
if (!pkm.InhabitedGeneration(8, species))
|
||||
return false;
|
||||
if (pkm.LA)
|
||||
return true;
|
||||
if (pkm.IsUntraded)
|
||||
return false;
|
||||
var pi = (PersonalInfoLA)PersonalTable.LA[species];
|
||||
return pi.IsPresentInGame;
|
||||
|
||||
var pt = PersonalTable.LA;
|
||||
foreach (var evo in evos)
|
||||
{
|
||||
if (pt.IsPresentInGame(evo.Species, evo.Form))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the moveset is restricted to only the original version.
|
||||
/// Checks if the moveset is restricted to only a specific version.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to check</param>
|
||||
/// <returns></returns>
|
||||
internal static bool IsMovesetRestricted(this PKM pkm)
|
||||
internal static (bool IsRestricted, GameVersion Game) IsMovesetRestricted(this PKM pkm) => pkm switch
|
||||
{
|
||||
if (pkm.IsUntraded)
|
||||
PB7 => (true, GameVersion.GP),
|
||||
PA8 => (true, GameVersion.PLA),
|
||||
PB8 => (true, GameVersion.BD),
|
||||
PK8 when pkm.Version > (int)GameVersion.SH => (true, GameVersion.SH), // Permit past generation moves.
|
||||
|
||||
IBattleVersion { BattleVersion: not 0 } bv => (true, (GameVersion)bv.BattleVersion),
|
||||
_ when pkm.IsUntraded => (true, (GameVersion)pkm.Version),
|
||||
_ => (false, GameVersion.Any),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the relearn moves should be wiped.
|
||||
/// </summary>
|
||||
/// <remarks>Already checked for generations < 8.</remarks>
|
||||
/// <param name="pkm">Entity to check</param>
|
||||
internal static bool IsOriginalMovesetDeleted(this PKM pkm)
|
||||
{
|
||||
if (pkm is PA8 {LA: false} or PB8 {BDSP: false})
|
||||
return true;
|
||||
if (pkm.BDSP)
|
||||
return true;
|
||||
if (pkm.LA)
|
||||
if (pkm.IsNative)
|
||||
{
|
||||
if (pkm is PK8 {LA: true} or PK8 {BDSP: true})
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pkm is IBattleVersion { BattleVersion: not 0 })
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -218,23 +285,6 @@ public static bool IsPPUpAvailable(PKM pkm)
|
|||
return pkm is not PA8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the moveset is restricted to only the original version.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Entity to check</param>
|
||||
/// <param name="gen">Generation the move check is for</param>
|
||||
/// <returns></returns>
|
||||
internal static bool IsMovesetRestricted(this PKM pkm, int gen)
|
||||
{
|
||||
if (pkm.IsMovesetRestricted())
|
||||
return true;
|
||||
return gen switch
|
||||
{
|
||||
7 when pkm.Version is (int)GameVersion.GO or (int)GameVersion.GP or (int)GameVersion.GE => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetMaxLengthOT(int generation, LanguageID language) => language switch
|
||||
{
|
||||
LanguageID.ChineseS or LanguageID.ChineseT => 6,
|
||||
|
|
@ -270,5 +320,12 @@ public static bool GetIsFixedIVSequenceValidNoRand(ReadOnlySpan<int> IVs, PKM pk
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsMetAsEgg(PKM pkm) => pkm switch
|
||||
{
|
||||
PA8 or PK8 => pkm.Egg_Location is not 0 || (pkm.BDSP && pkm.Egg_Day is not 0),
|
||||
PB8 pb8 => pb8.Egg_Location is not Locations.Default8bNone,
|
||||
_ => pkm.Egg_Location is not 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,11 +41,13 @@ private static bool IsValidDate(DateTime obtained, (DateTime Start, DateTime End
|
|||
{
|
||||
WC8 wc8 => Result(IsValidDateWC8(wc8.CardID, obtained)),
|
||||
WA8 wa8 => Result(IsValidDateWA8(wa8.CardID, obtained)),
|
||||
WB8 wb8 => Result(IsValidDateWB8(wb8.CardID, obtained)),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(enc)),
|
||||
};
|
||||
|
||||
public static bool IsValidDateWC8(int cardID, DateTime obtained) => WC8Gifts.TryGetValue(cardID, out var time) && IsValidDate(obtained, time);
|
||||
public static bool IsValidDateWA8(int cardID, DateTime obtained) => WA8Gifts.TryGetValue(cardID, out var time) && IsValidDate(obtained, time);
|
||||
public static bool IsValidDateWB8(int cardID, DateTime obtained) => WB8Gifts.TryGetValue(cardID, out var time) && IsValidDate(obtained, time);
|
||||
|
||||
/// <summary>
|
||||
/// Minimum date the gift can be received.
|
||||
|
|
@ -69,13 +71,31 @@ private static bool IsValidDate(DateTime obtained, (DateTime Start, DateTime End
|
|||
{9014, new DateTime(2021, 06, 17)}, // Gigantamax Squirtle
|
||||
};
|
||||
|
||||
private static readonly DateTime Never = DateTime.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum date the gift can be received.
|
||||
/// </summary>
|
||||
public static readonly Dictionary<int, (DateTime Start, DateTime End)> WA8Gifts = new()
|
||||
{
|
||||
{0138, (new(2022, 01, 27), new(2022, 11, 01))}, // Poké Center Happiny
|
||||
{0138, (new(2022, 01, 27), new(2023, 02, 01))}, // Poké Center Happiny
|
||||
{0301, (new(2022, 02, 04), new(2022, 02, 24))}, // プロポチャ Piplup
|
||||
{0801, (new(2022, 02, 25), new(2022, 06, 01))}, // Teresa Roca Hisuian Growlithe
|
||||
{1201, (new(2022, 05, 31), new(2022, 08, 01))}, // 전이마을 Regigigas
|
||||
{1202, (new(2022, 05, 31), new(2022, 08, 01))}, // 빛나's Piplup
|
||||
|
||||
{9018, (new(2022, 05, 18), Never)}, // Hidden Ability Rowlet
|
||||
{9019, (new(2022, 05, 18), Never)}, // Hidden Ability Cyndaquil
|
||||
{9020, (new(2022, 05, 18), Never)}, // Hidden Ability Oshawott
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Minimum date the gift can be received.
|
||||
/// </summary>
|
||||
public static readonly Dictionary<int, (DateTime Start, DateTime End)> WB8Gifts = new()
|
||||
{
|
||||
{9015, (new(2022, 05, 18), Never)}, // Hidden Ability Turtwig
|
||||
{9016, (new(2022, 05, 18), Never)}, // Hidden Ability Chimchar
|
||||
{9017, (new(2022, 05, 18), Never)}, // Hidden Ability Piplup
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,12 +74,14 @@ public virtual string LongName
|
|||
|
||||
public PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
|
||||
{
|
||||
var pk = EntityBlank.GetBlank(Generation, Version);
|
||||
var pk = GetBlank();
|
||||
sav.ApplyTo(pk);
|
||||
ApplyDetails(sav, criteria, pk);
|
||||
return pk;
|
||||
}
|
||||
|
||||
protected virtual PKM GetBlank() => EntityBlank.GetBlank(Generation, Version);
|
||||
|
||||
protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
|
||||
{
|
||||
var version = this.GetCompatibleVersion((GameVersion) sav.Game);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public override string GetConditionString(out bool valid)
|
|||
|
||||
private int[] GetDexNavMoves()
|
||||
{
|
||||
var et = EvolutionTree.GetEvolutionTree(6);
|
||||
var et = EvolutionTree.Evolves6;
|
||||
var sf = et.GetBaseSpeciesForm(Species, Form);
|
||||
return MoveEgg.GetEggMoves(6, sf & 0x7FF, sf >> 11, Version);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public EncounterSlot7GO(EncounterArea7g area, int species, int form, int start,
|
|||
{
|
||||
}
|
||||
|
||||
protected override PKM GetBlank() => new PB7();
|
||||
|
||||
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
|
||||
{
|
||||
base.ApplyDetails(sav, criteria, pk);
|
||||
|
|
@ -27,6 +29,21 @@ protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteri
|
|||
pb.ResetCP();
|
||||
}
|
||||
|
||||
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
|
||||
{
|
||||
var pi = pk.PersonalInfo;
|
||||
int gender = criteria.GetGender(-1, pi);
|
||||
int nature = (int)criteria.GetNature(Nature.Random);
|
||||
var ability = criteria.GetAbilityFromNumber(Ability);
|
||||
|
||||
pk.PID = Util.Rand32();
|
||||
pk.Nature = pk.StatNature = nature;
|
||||
pk.Gender = gender;
|
||||
pk.RefreshAbility(ability);
|
||||
pk.SetRandomIVsGO();
|
||||
base.SetPINGA(pk, criteria);
|
||||
}
|
||||
|
||||
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
|
||||
{
|
||||
var moves = MoveLevelUp.GetEncounterMoves(pk, level, GameVersion.GG);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Encounter Slot representing data transferred to <see cref="GameVersion.Gen8"/> (HOME).
|
||||
/// <inheritdoc cref="EncounterSlotGO" />
|
||||
/// </summary>
|
||||
public sealed record EncounterSlot8GO : EncounterSlotGO
|
||||
public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship
|
||||
{
|
||||
public override int Generation => 8;
|
||||
|
||||
|
|
@ -13,14 +15,14 @@ public sealed record EncounterSlot8GO : EncounterSlotGO
|
|||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Future game releases might change this value.
|
||||
/// With respect to date legality, new dates might be incompatible with initial <seealso cref="OriginGroup"/> values.
|
||||
/// With respect to date legality, new dates might be incompatible with initial <seealso cref="OriginFormat"/> values.
|
||||
/// </remarks>
|
||||
public GameVersion OriginGroup { get; }
|
||||
public PogoImportFormat OriginFormat { get; }
|
||||
|
||||
public EncounterSlot8GO(EncounterArea8g area, int species, int form, int start, int end, Shiny shiny, Gender gender, PogoType type, GameVersion originGroup)
|
||||
public EncounterSlot8GO(EncounterArea8g area, int species, int form, int start, int end, Shiny shiny, Gender gender, PogoType type, PogoImportFormat originFormat)
|
||||
: base(area, species, form, start, end, shiny, gender, type)
|
||||
{
|
||||
OriginGroup = originGroup;
|
||||
OriginFormat = originFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,21 +36,84 @@ public bool IsBallValid(Ball ball, int currentSpecies)
|
|||
return Type.IsBallValid(ball);
|
||||
}
|
||||
|
||||
protected override PKM GetBlank() => OriginFormat switch
|
||||
{
|
||||
PogoImportFormat.PK7 => new PK8(),
|
||||
PogoImportFormat.PB7 => new PB7(),
|
||||
PogoImportFormat.PK8 => new PK8(),
|
||||
PogoImportFormat.PA8 => new PA8(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(OriginFormat)),
|
||||
};
|
||||
|
||||
private PersonalInfo GetPersonal() => OriginFormat switch
|
||||
{
|
||||
PogoImportFormat.PK7 => PersonalTable.USUM.GetFormEntry(Species, Form),
|
||||
PogoImportFormat.PB7 => PersonalTable.GG.GetFormEntry(Species, Form),
|
||||
PogoImportFormat.PK8 => PersonalTable.SWSH.GetFormEntry(Species, Form),
|
||||
PogoImportFormat.PA8 => PersonalTable.LA.GetFormEntry(Species, Form),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(OriginFormat)),
|
||||
};
|
||||
|
||||
internal GameVersion OriginGroup => OriginFormat switch
|
||||
{
|
||||
PogoImportFormat.PK7 => GameVersion.USUM,
|
||||
PogoImportFormat.PB7 => GameVersion.GG,
|
||||
PogoImportFormat.PK8 => GameVersion.SWSH,
|
||||
PogoImportFormat.PA8 => GameVersion.PLA,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(OriginFormat)),
|
||||
};
|
||||
|
||||
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
|
||||
{
|
||||
var pk8 = (PK8)pk;
|
||||
pk8.HT_Name = "PKHeX";
|
||||
pk8.HT_Language = 2;
|
||||
pk8.CurrentHandler = 1;
|
||||
pk.HT_Name = "PKHeX";
|
||||
pk.CurrentHandler = 1;
|
||||
if (pk is IHandlerLanguage l)
|
||||
l.HT_Language = 2;
|
||||
|
||||
base.ApplyDetails(sav, criteria, pk);
|
||||
var ball = Type.GetValidBall();
|
||||
if (ball != Ball.None)
|
||||
pk.Ball = (int)ball;
|
||||
|
||||
pk8.SetRandomEC();
|
||||
pk8.HeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
pk8.WeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
if (pk is IScaledSize s)
|
||||
{
|
||||
s.HeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
s.WeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
}
|
||||
|
||||
if (OriginFormat is PogoImportFormat.PA8)
|
||||
{
|
||||
var pa8 = (PA8)pk;
|
||||
pa8.ResetHeight();
|
||||
pa8.ResetWeight();
|
||||
pa8.HeightScalarCopy = pa8.HeightScalar;
|
||||
}
|
||||
|
||||
pk.OT_Friendship = OT_Friendship;
|
||||
|
||||
pk.SetRandomEC();
|
||||
}
|
||||
|
||||
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
|
||||
{
|
||||
var pi = GetPersonal();
|
||||
if (OriginFormat is PogoImportFormat.PK7)
|
||||
pk.EXP = Experience.GetEXP(LevelMin, pi.EXPGrowth);
|
||||
int gender = criteria.GetGender(-1, pi);
|
||||
int nature = (int)criteria.GetNature(Nature.Random);
|
||||
var ability = criteria.GetAbilityFromNumber(Ability);
|
||||
|
||||
pk.PID = Util.Rand32();
|
||||
pk.Nature = pk.StatNature = nature;
|
||||
pk.Gender = gender;
|
||||
|
||||
pk.AbilityNumber = 1 << ability;
|
||||
var abilities = pi.Abilities;
|
||||
if ((uint)ability < abilities.Count)
|
||||
pk.Ability = abilities[ability];
|
||||
|
||||
pk.SetRandomIVsGO();
|
||||
base.SetPINGA(pk, criteria);
|
||||
}
|
||||
|
||||
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
|
||||
|
|
@ -65,6 +130,22 @@ public override EncounterMatchRating GetMatchRating(PKM pkm)
|
|||
return base.GetMatchRating(pkm) == EncounterMatchRating.PartialMatch ? EncounterMatchRating.PartialMatch : EncounterMatchRating.Match;
|
||||
}
|
||||
|
||||
public byte OT_Friendship => Species switch
|
||||
{
|
||||
(int)Core.Species.Timburr when Form == 0 => 70,
|
||||
(int)Core.Species.Stunfisk when Form == 0 => 70,
|
||||
(int)Core.Species.Hoopa when Form == 1 => 50,
|
||||
_ => GetHOMEFriendship(),
|
||||
};
|
||||
|
||||
private byte GetHOMEFriendship()
|
||||
{
|
||||
var fs = (byte)GetPersonal().BaseFriendship;
|
||||
if (fs == 70)
|
||||
return 50;
|
||||
return fs;
|
||||
}
|
||||
|
||||
private bool IsMatchPartial(PKM pk)
|
||||
{
|
||||
var stamp = GetTimeStamp(pk.Met_Year + 2000, pk.Met_Month, pk.Met_Day);
|
||||
|
|
@ -76,19 +157,33 @@ private bool IsMatchPartial(PKM pk)
|
|||
return true;
|
||||
|
||||
// Eevee & Glaceon have different base friendships. Make sure if it is invalid that we yield the other encounter before.
|
||||
if (PersonalTable.SWSH.GetFormEntry(Species, Form).BaseFriendship != pk.OT_Friendship)
|
||||
if (pk.OT_Friendship != OT_Friendship)
|
||||
return true;
|
||||
|
||||
return Species switch
|
||||
{
|
||||
(int)Core.Species.Yamask when pk.Species != Species && Form == 1 => pk is IFormArgument { FormArgument: 0 },
|
||||
(int)Core.Species.Milcery when pk.Species != Species => pk is IFormArgument { FormArgument: 0 },
|
||||
(int)Core.Species.Qwilfish when pk.Species != Species && Form == 1 => pk is IFormArgument { FormArgument: 0 },
|
||||
(int)Core.Species.Basculin when pk.Species != Species && Form == 2 => pk is IFormArgument { FormArgument: 0 },
|
||||
(int)Core.Species.Stantler when pk.Species != Species => pk is IFormArgument { FormArgument: 0 },
|
||||
|
||||
(int)Core.Species.Runerigus => pk is IFormArgument { FormArgument: not 0 },
|
||||
(int)Core.Species.Alcremie => pk is IFormArgument { FormArgument: not 0 },
|
||||
(int)Core.Species.Wyrdeer => pk is IFormArgument { FormArgument: not 0 },
|
||||
(int)Core.Species.Basculegion => pk is IFormArgument { FormArgument: not 0 },
|
||||
(int)Core.Species.Overqwil => pk is IFormArgument { FormArgument: not 0 },
|
||||
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public enum PogoImportFormat : byte
|
||||
{
|
||||
PK7 = 0,
|
||||
PB7 = 1,
|
||||
PK8 = 2,
|
||||
PA8 = 3,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,6 @@ public bool GetIVsValid(PKM pkm)
|
|||
|
||||
protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
|
||||
{
|
||||
base.SetPINGA(pk, criteria);
|
||||
switch (Shiny)
|
||||
{
|
||||
case Shiny.Random when !pk.IsShiny && criteria.Shiny.IsShiny():
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IM
|
|||
public IReadOnlyList<int> Moves { get; init; } = Array.Empty<int>();
|
||||
public IReadOnlyList<int> IVs { get; init; } = Array.Empty<int>();
|
||||
|
||||
public bool EggEncounter => EggLocation > 0;
|
||||
public virtual bool EggEncounter => EggLocation != 0;
|
||||
|
||||
private const string _name = "Static Encounter";
|
||||
public string Name => _name;
|
||||
|
|
@ -107,9 +107,9 @@ protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria
|
|||
s.HeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
s.WeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
}
|
||||
if (this is IGigantamax g && pk is IGigantamax pg)
|
||||
if (this is IGigantamax g && pk is PK8 pg)
|
||||
pg.CanGigantamax = g.CanGigantamax;
|
||||
if (this is IDynamaxLevel d && pk is IDynamaxLevel pd)
|
||||
if (this is IDynamaxLevel d && pk is PK8 pd)
|
||||
pd.DynamaxLevel = d.DynamaxLevel;
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +264,11 @@ protected virtual bool IsMatchForm(PKM pkm, EvoCriteria evo)
|
|||
}
|
||||
|
||||
// override me if the encounter type has any eggs
|
||||
protected virtual bool IsMatchEggLocation(PKM pkm) => pkm.Egg_Location == 0;
|
||||
protected virtual bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var expect = pkm is PB8 ? Locations.Default8bNone : 0;
|
||||
return pkm.Egg_Location == expect;
|
||||
}
|
||||
|
||||
private bool IsMatchGender(PKM pkm)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ protected override bool IsMatchEggLocation(PKM pkm)
|
|||
{
|
||||
if (pkm.Format == 3)
|
||||
return !pkm.IsEgg || EggLocation == 0 || EggLocation == pkm.Met_Location;
|
||||
return pkm.Egg_Location == 0;
|
||||
return base.IsMatchEggLocation(pkm);
|
||||
}
|
||||
|
||||
protected override bool IsMatchLevel(PKM pkm, EvoCriteria evo)
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ protected override bool IsMatchLocation(PKM pkm)
|
|||
|
||||
protected override bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!EggEncounter)
|
||||
return eggloc == 0;
|
||||
return base.IsMatchEggLocation(pkm);
|
||||
|
||||
var eggloc = pkm.Egg_Location;
|
||||
// Transferring 4->5 clears Pt/HG/SS location value and keeps Faraway Place
|
||||
if (pkm is not G4PKM pk4)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ protected sealed override bool IsMatchLocation(PKM pk)
|
|||
|
||||
protected override bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!EggEncounter)
|
||||
return eggloc == EggLocation;
|
||||
return base.IsMatchEggLocation(pkm);
|
||||
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!pkm.IsEgg) // hatched
|
||||
return eggloc == EggLocation || eggloc == Locations.LinkTrade5;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ protected override bool IsMatchLocation(PKM pkm)
|
|||
|
||||
protected override bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!EggEncounter)
|
||||
return eggloc == EggLocation;
|
||||
return base.IsMatchEggLocation(pkm);
|
||||
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!pkm.IsEgg) // hatched
|
||||
return eggloc == EggLocation || eggloc == Locations.LinkTrade6;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ protected override bool IsMatchLocation(PKM pkm)
|
|||
|
||||
protected override bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!EggEncounter)
|
||||
return eggloc == EggLocation;
|
||||
return base.IsMatchEggLocation(pkm);
|
||||
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!pkm.IsEgg) // hatched
|
||||
return eggloc == EggLocation || eggloc == Locations.LinkTrade6;
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ protected override bool IsMatchLevel(PKM pkm, EvoCriteria evo)
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm is IDynamaxLevel d && d.DynamaxLevel < DynamaxLevel)
|
||||
if (pkm is PK8 d && d.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
if (pkm.Met_Level < EncounterArea8.BoostLevel && Weather is AreaWeather8.Heavy_Fog && EncounterArea8.IsBoostedArea60Fog(Location))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ public abstract record EncounterStatic8Nest<T>(GameVersion Version) : EncounterS
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm is IDynamaxLevel d && d.DynamaxLevel < DynamaxLevel)
|
||||
if (pkm is PK8 d && d.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
|
||||
// Required Ability
|
||||
if (Ability == AbilityPermission.OnlyHidden && pkm.AbilityNumber != 4)
|
||||
if (Ability == OnlyHidden && pkm.AbilityNumber != 4)
|
||||
return false; // H
|
||||
|
||||
if (Version != GameVersion.SWSH && pkm.Version != (int)Version && pkm.Met_Location != SharedNest)
|
||||
|
|
@ -64,7 +64,7 @@ protected sealed override EncounterMatchRating IsMatchDeferred(PKM pkm)
|
|||
|
||||
protected override bool IsMatchPartial(PKM pkm)
|
||||
{
|
||||
if (pkm is IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
|
||||
if (pkm is PK8 and IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
|
||||
return true;
|
||||
if (Species == (int)Core.Species.Alcremie && pkm is IFormArgument { FormArgument: not 0 })
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -83,6 +83,16 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override bool IsMatchLocation(PKM pkm)
|
||||
{
|
||||
if (pkm is PK8)
|
||||
return pkm.Met_Location == Locations.HOME_SWLA;
|
||||
if (pkm is PB8 { Version: (int)GameVersion.PLA, Met_Location: Locations.HOME_SWLA })
|
||||
return true;
|
||||
|
||||
return base.IsMatchLocation(pkm);
|
||||
}
|
||||
|
||||
public override EncounterMatchRating GetMatchRating(PKM pkm)
|
||||
{
|
||||
var result = GetMatchRatingInternal(pkm);
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ public sealed record EncounterStatic8b : EncounterStatic, IStaticCorrelation8b
|
|||
public override int Generation => 8;
|
||||
|
||||
public bool Roaming { get; init; }
|
||||
public override bool EggEncounter => EggLocation != Locations.Default8bNone;
|
||||
|
||||
public EncounterStatic8b(GameVersion game) : base(game)
|
||||
{
|
||||
EggLocation = Locations.Default8bNone;
|
||||
}
|
||||
public EncounterStatic8b(GameVersion game) : base(game) => EggLocation = Locations.Default8bNone;
|
||||
|
||||
protected override bool IsMatchLocation(PKM pkm)
|
||||
{
|
||||
if (pkm is PK8)
|
||||
return Locations.IsValidMetBDSP(pkm.Met_Location, pkm.Version);
|
||||
if (!Roaming)
|
||||
return base.IsMatchLocation(pkm);
|
||||
return IsRoamingLocation(pkm);
|
||||
|
|
@ -47,17 +47,34 @@ public bool IsStaticCorrelationCorrect(PKM pk)
|
|||
|
||||
protected override bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var eggloc = (short)pkm.Egg_Location;
|
||||
if (pkm is not PB8)
|
||||
{
|
||||
if (!EggEncounter)
|
||||
return pkm.Egg_Location == 0;
|
||||
|
||||
if (pkm is PK8)
|
||||
{
|
||||
if (EggLocation > 60000 && pkm.Egg_Location == Locations.HOME_SWSHBDSPEgg)
|
||||
return true;
|
||||
// >60000 can be reset to Link Trade (30001), then altered differently.
|
||||
return Locations.IsValidMetBDSP(pkm.Egg_Location, pkm.Version);
|
||||
}
|
||||
|
||||
// Hatched
|
||||
return pkm.Egg_Location == EggLocation || pkm.Egg_Location == Locations.LinkTrade6NPC;
|
||||
}
|
||||
|
||||
var eggloc = pkm.Egg_Location;
|
||||
if (!EggEncounter)
|
||||
return eggloc == (short)EggLocation;
|
||||
return eggloc == EggLocation;
|
||||
|
||||
if (!pkm.IsEgg) // hatched
|
||||
return eggloc == (short)EggLocation || eggloc == Locations.LinkTrade6NPC;
|
||||
return eggloc == EggLocation || eggloc == Locations.LinkTrade6NPC;
|
||||
|
||||
// Unhatched:
|
||||
if (eggloc != (short)EggLocation)
|
||||
if (eggloc != EggLocation)
|
||||
return false;
|
||||
if ((short)pkm.Met_Location is not (Locations.Default8bNone or Locations.LinkTrade6NPC))
|
||||
if (pkm.Met_Location is not (Locations.Default8bNone or Locations.LinkTrade6NPC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ public virtual bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
return false;
|
||||
if (OTGender != -1 && OTGender != pkm.OT_Gender)
|
||||
return false;
|
||||
if (EggLocation != pkm.Egg_Location)
|
||||
if (!IsMatchEggLocation(pkm))
|
||||
return false;
|
||||
// if (z.Ability == 4 ^ pkm.AbilityNumber == 4) // defer to Ability
|
||||
// continue;
|
||||
|
|
@ -213,6 +213,14 @@ public virtual bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var expect = EggLocation;
|
||||
if (pkm is PB8 && expect is 0)
|
||||
expect = Locations.Default8bNone;
|
||||
return pkm.Egg_Location == expect;
|
||||
}
|
||||
|
||||
private bool IsMatchLevel(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (!pkm.HasOriginalMetLocation)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public EncounterTrade8(GameVersion game, int species, byte level, byte memory, u
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm is IDynamaxLevel d && d.DynamaxLevel < DynamaxLevel)
|
||||
if (pkm is PK8 d && d.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
if (pkm.FlawlessIVCount < FlawlessIVCount)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ public sealed record EncounterTrade8b : EncounterTrade, IContestStats, IScaledSi
|
|||
public override int Generation => 8;
|
||||
public override int Location => Locations.LinkTrade6NPC;
|
||||
|
||||
public EncounterTrade8b(GameVersion game) : base(game) => EggLocation = unchecked((ushort)Locations.Default8bNone);
|
||||
public EncounterTrade8b(GameVersion game) : base(game) => EggLocation = Locations.Default8bNone;
|
||||
public byte CNT_Cool => BaseContest;
|
||||
public byte CNT_Beauty => BaseContest;
|
||||
public byte CNT_Cute => BaseContest;
|
||||
|
|
@ -38,6 +38,14 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
return base.IsMatchExact(pkm, evo);
|
||||
}
|
||||
|
||||
protected override bool IsMatchEggLocation(PKM pkm)
|
||||
{
|
||||
var expect = EggLocation;
|
||||
if (pkm is not PB8 && expect == Locations.Default8bNone)
|
||||
expect = 0;
|
||||
return pkm.Egg_Location == expect;
|
||||
}
|
||||
|
||||
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
|
||||
{
|
||||
base.ApplyDetails(sav, criteria, pk);
|
||||
|
|
|
|||
|
|
@ -79,8 +79,9 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3CXD(PKM pkm)
|
|||
{
|
||||
var chain = EncounterOrigin.GetOriginChain(pkm);
|
||||
|
||||
var game = (GameVersion)pkm.Version;
|
||||
// Mystery Gifts
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{
|
||||
// Don't bother deferring matches.
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -89,7 +90,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3CXD(PKM pkm)
|
|||
}
|
||||
|
||||
// Trades
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
// Don't bother deferring matches.
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -100,7 +101,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3CXD(PKM pkm)
|
|||
IEncounterable? partial = null;
|
||||
|
||||
// Static Encounter
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
@ -110,7 +111,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3CXD(PKM pkm)
|
|||
}
|
||||
|
||||
// Encounter Slots
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
@ -128,9 +129,10 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3CXD(PKM pkm)
|
|||
private static IEnumerable<IEncounterable> GenerateRawEncounters3(PKM pkm, LegalInfo info)
|
||||
{
|
||||
var chain = EncounterOrigin.GetOriginChain(pkm);
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
// Mystery Gifts
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{
|
||||
// Don't bother deferring matches.
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -139,7 +141,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3(PKM pkm, Legal
|
|||
}
|
||||
|
||||
// Trades
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
// Don't bother deferring matches.
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -155,7 +157,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3(PKM pkm, Legal
|
|||
bool safari = pkm.Ball == 0x05; // never static encounters
|
||||
if (!safari)
|
||||
{
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
@ -167,7 +169,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3(PKM pkm, Legal
|
|||
|
||||
// Encounter Slots
|
||||
var slots = FrameFinder.GetFrames(info.PIDIV, pkm).ToList();
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
@ -208,7 +210,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters3(PKM pkm, Legal
|
|||
|
||||
partial = null;
|
||||
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
|
|||
|
|
@ -42,19 +42,20 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, LegalInfo info)
|
|||
private static IEnumerable<IEncounterable> GenerateRawEncounters4(PKM pkm, LegalInfo info)
|
||||
{
|
||||
var chain = EncounterOrigin.GetOriginChain(pkm);
|
||||
var game = (GameVersion)pkm.Version;
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
int ctr = 0;
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
if (Locations.IsEggLocationBred4(pkm.Egg_Location, (GameVersion)pkm.Version))
|
||||
if (Locations.IsEggLocationBred4(pkm.Egg_Location, game))
|
||||
{
|
||||
foreach (var z in GenerateEggs(pkm, 4))
|
||||
yield return z;
|
||||
}
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
yield return z;
|
||||
|
||||
IEncounterable? deferred = null;
|
||||
|
|
@ -63,7 +64,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters4(PKM pkm, Legal
|
|||
bool safariSport = pkm.Ball is (int)Ball.Sport or (int)Ball.Safari; // never static encounters
|
||||
if (!safariSport)
|
||||
{
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
@ -74,7 +75,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters4(PKM pkm, Legal
|
|||
}
|
||||
|
||||
var slots = FrameFinder.GetFrames(info.PIDIV, pkm).ToList();
|
||||
foreach (var slot in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var slot in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var z = (EncounterSlot4)slot;
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -115,7 +116,7 @@ private static IEnumerable<IEncounterable> GenerateRawEncounters4(PKM pkm, Legal
|
|||
if (!safariSport)
|
||||
yield break;
|
||||
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == PartialMatch)
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
int ctr = 0;
|
||||
|
||||
var chain = EncounterOrigin.GetOriginChain(pkm);
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
|
@ -33,7 +35,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
IEncounterable? deferred = null;
|
||||
IEncounterable? partial = null;
|
||||
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -45,7 +47,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -57,7 +59,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
|
|||
|
|
@ -16,13 +16,14 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
int ctr = 0;
|
||||
|
||||
var chain = EncounterOrigin.GetOriginChain(pkm);
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
IEncounterable? deferred = null;
|
||||
IEncounterable? partial = null;
|
||||
|
||||
if (pkm.FatefulEncounter || pkm.Met_Location == Locations.LinkGift6)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -53,7 +54,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
if (ctr == 0) yield break;
|
||||
}
|
||||
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -65,7 +66,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -77,7 +78,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ internal static IEnumerable<IEncounterable> GetEncountersGO(PKM pkm, EvoCriteria
|
|||
IEncounterable? partial = null;
|
||||
|
||||
int ctr = 0;
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, GameVersion.GO))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -50,9 +50,11 @@ internal static IEnumerable<IEncounterable> GetEncountersGO(PKM pkm, EvoCriteria
|
|||
private static IEnumerable<IEncounterable> GetEncountersGG(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
int ctr = 0;
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
|
@ -60,7 +62,7 @@ private static IEnumerable<IEncounterable> GetEncountersGG(PKM pkm, EvoCriteria[
|
|||
IEncounterable? deferred = null;
|
||||
IEncounterable? partial = null;
|
||||
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -72,7 +74,7 @@ private static IEnumerable<IEncounterable> GetEncountersGG(PKM pkm, EvoCriteria[
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -84,7 +86,7 @@ private static IEnumerable<IEncounterable> GetEncountersGG(PKM pkm, EvoCriteria[
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -105,9 +107,11 @@ private static IEnumerable<IEncounterable> GetEncountersGG(PKM pkm, EvoCriteria[
|
|||
private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
int ctr = 0;
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
|
@ -122,7 +126,7 @@ private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCri
|
|||
IEncounterable? deferred = null;
|
||||
IEncounterable? partial = null;
|
||||
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain))
|
||||
foreach (var z in GetValidStaticEncounter(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -134,7 +138,7 @@ private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCri
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
@ -146,7 +150,7 @@ private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCri
|
|||
}
|
||||
if (ctr != 0) yield break;
|
||||
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
switch (match)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
(int)GameVersion.GO => EncounterGenerator7.GetEncountersGO(pkm, chain),
|
||||
(int)GameVersion.PLA => EncounterGenerator8a.GetEncounters(pkm, chain),
|
||||
(int)GameVersion.BD or (int)GameVersion.SP => EncounterGenerator8b.GetEncounters(pkm, chain),
|
||||
(int)GameVersion.SW when pkm.Met_Location == Locations.HOME_SWLA => EncounterGenerator8a.GetEncounters(pkm, chain),
|
||||
(int)GameVersion.SW when pkm.Met_Location == Locations.HOME_SWBD => EncounterGenerator8b.GetEncountersFuzzy(pkm, chain, GameVersion.BD),
|
||||
(int)GameVersion.SH when pkm.Met_Location == Locations.HOME_SHSP => EncounterGenerator8b.GetEncountersFuzzy(pkm, chain, GameVersion.SP),
|
||||
_ => GetEncountersMainline(pkm, chain),
|
||||
};
|
||||
}
|
||||
|
|
@ -26,9 +29,11 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
|
|||
private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
int ctr = 0;
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
|
@ -46,7 +51,7 @@ private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCri
|
|||
// Trades
|
||||
if (pkm.Met_Location == Locations.LinkTrade6NPC)
|
||||
{
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
|
|
@ -66,7 +71,7 @@ private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCri
|
|||
}
|
||||
|
||||
// Static Encounters can collide with wild encounters (close match); don't break if a Static Encounter is yielded.
|
||||
var encs = GetValidStaticEncounter(pkm, chain);
|
||||
var encs = GetValidStaticEncounter(pkm, chain, game);
|
||||
foreach (var z in encs)
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -81,7 +86,7 @@ private static IEnumerable<IEncounterable> GetEncountersMainline(PKM pkm, EvoCri
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@ internal static class EncounterGenerator8a
|
|||
{
|
||||
public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
if (pkm is PK8 { SWSH: false })
|
||||
yield break;
|
||||
if (pkm.IsEgg)
|
||||
yield break;
|
||||
|
||||
int ctr = 0;
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, GameVersion.PLA))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
|
@ -26,7 +28,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] c
|
|||
EncounterMatchRating rating = None;
|
||||
|
||||
// Static Encounters can collide with wild encounters (close match); don't break if a Static Encounter is yielded.
|
||||
var encs = GetValidStaticEncounter(pkm, chain);
|
||||
var encs = GetValidStaticEncounter(pkm, chain, GameVersion.PLA);
|
||||
foreach (var z in encs)
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -41,7 +43,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] c
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, GameVersion.PLA))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
|
|
|
|||
|
|
@ -13,10 +13,14 @@ internal static class EncounterGenerator8b
|
|||
{
|
||||
public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
if (pkm is PK8)
|
||||
yield break;
|
||||
int ctr = 0;
|
||||
var game = (GameVersion)pkm.Version;
|
||||
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain))
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
|
@ -34,7 +38,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] c
|
|||
// Trades
|
||||
if (!pkm.IsEgg && pkm.Met_Location == Locations.LinkTrade6NPC)
|
||||
{
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain))
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
|
|
@ -54,7 +58,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] c
|
|||
}
|
||||
|
||||
// Static Encounters can collide with wild encounters (close match); don't break if a Static Encounter is yielded.
|
||||
var encs = GetValidStaticEncounter(pkm, chain);
|
||||
var encs = GetValidStaticEncounter(pkm, chain, game);
|
||||
foreach (var z in encs)
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
|
|
@ -69,7 +73,83 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, EvoCriteria[] c
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain))
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
{
|
||||
yield return z;
|
||||
}
|
||||
else if (match < rating)
|
||||
{
|
||||
cache = z;
|
||||
rating = match;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache != null)
|
||||
yield return cache;
|
||||
}
|
||||
|
||||
public static IEnumerable<IEncounterable> GetEncountersFuzzy(PKM pkm, EvoCriteria[] chain, GameVersion game)
|
||||
{
|
||||
int ctr = 0;
|
||||
|
||||
if (pkm.FatefulEncounter)
|
||||
{
|
||||
foreach (var z in GetValidGifts(pkm, chain, game))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr != 0) yield break;
|
||||
}
|
||||
|
||||
if (pkm.Egg_Location == Locations.HOME_SWSHBDSPEgg && pkm.Met_Level == 1)
|
||||
{
|
||||
foreach (var z in GenerateEggs(pkm, 8))
|
||||
{ yield return z; ++ctr; }
|
||||
if (ctr == 0) yield break;
|
||||
}
|
||||
|
||||
IEncounterable? cache = null;
|
||||
EncounterMatchRating rating = None;
|
||||
|
||||
// Trades
|
||||
if (!pkm.IsEgg)
|
||||
{
|
||||
foreach (var z in GetValidEncounterTrades(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
{
|
||||
yield return z;
|
||||
}
|
||||
else if (match < rating)
|
||||
{
|
||||
cache = z;
|
||||
rating = match;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache != null)
|
||||
yield return cache;
|
||||
}
|
||||
|
||||
// Static Encounters can collide with wild encounters (close match); don't break if a Static Encounter is yielded.
|
||||
var encs = GetValidStaticEncounter(pkm, chain, game);
|
||||
foreach (var z in encs)
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
{
|
||||
yield return z;
|
||||
}
|
||||
else if (match < rating)
|
||||
{
|
||||
cache = z;
|
||||
rating = match;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var z in GetValidWildEncounters(pkm, chain, game))
|
||||
{
|
||||
var match = z.GetMatchRating(pkm);
|
||||
if (match == Match)
|
||||
|
|
|
|||
|
|
@ -173,18 +173,18 @@ private static string GetHintWhyNotFound(PKM pkm, int gen)
|
|||
|
||||
private static bool WasEventEgg(PKM pkm, int gen) => gen switch
|
||||
{
|
||||
// Event Egg, indistinguible from normal eggs after hatch
|
||||
// Event Egg, indistinguishable from normal eggs after hatch
|
||||
// can't tell after transfer
|
||||
3 => pkm.Format == 3 && pkm.IsEgg && pkm.Met_Location == 255,
|
||||
3 => pkm.Format == 3 && pkm.IsEgg && Locations.IsEventLocation3(pkm.Met_Location),
|
||||
|
||||
// Manaphy was the only generation 4 released event egg
|
||||
_ => pkm.Egg_Location is not 0 && pkm.FatefulEncounter,
|
||||
_ => pkm.FatefulEncounter && pkm.Egg_Day != 0,
|
||||
};
|
||||
|
||||
private static bool WasEvent(PKM pkm, int gen) => pkm.FatefulEncounter || gen switch
|
||||
{
|
||||
3 => (pkm.Met_Location == 255 && pkm.Format == 3),
|
||||
4 => (Locations.IsEventLocation4(pkm.Met_Location) && pkm.Format == 4),
|
||||
3 => Locations.IsEventLocation3(pkm.Met_Location) && pkm.Format == 3,
|
||||
4 => Locations.IsEventLocation4(pkm.Met_Location) && pkm.Format == 4,
|
||||
>=5 => Locations.IsEventLocation5(pkm.Met_Location),
|
||||
_ => false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -142,10 +142,10 @@ public static IEnumerable<IEncounterable> GenerateVersionEncounters(PKM pk, IEnu
|
|||
if (pk.Species == 0) // can enter this method after failing to set a species ID that cannot exist in the format
|
||||
return Array.Empty<IEncounterable>();
|
||||
pk.Version = (int)version;
|
||||
var format = pk.Format;
|
||||
if (format is 2 && version is GameVersion.RD or GameVersion.GN or GameVersion.BU or GameVersion.YW)
|
||||
format = 1; // try excluding baby pokemon from our evolution chain, for move learning purposes.
|
||||
var et = EvolutionTree.GetEvolutionTree(pk, format);
|
||||
var context = pk.Context;
|
||||
if (context is EntityContext.Gen2 && version is GameVersion.RD or GameVersion.GN or GameVersion.BU or GameVersion.YW)
|
||||
context = EntityContext.Gen1; // try excluding baby pokemon from our evolution chain, for move learning purposes.
|
||||
var et = EvolutionTree.GetEvolutionTree(context);
|
||||
var chain = et.GetValidPreEvolutions(pk, maxLevel: 100, skipChecks: true);
|
||||
int[] needs = GetNeededMoves(pk, moves, chain);
|
||||
|
||||
|
|
@ -225,7 +225,7 @@ private static IEnumerable<IEncounterable> GetPossibleOfType(PKM pk, IReadOnlyLi
|
|||
return type switch
|
||||
{
|
||||
EncounterOrder.Egg => GetEggs(pk, needs, chain, version),
|
||||
EncounterOrder.Mystery => GetGifts(pk, needs, chain),
|
||||
EncounterOrder.Mystery => GetGifts(pk, needs, chain, version),
|
||||
EncounterOrder.Static => GetStatic(pk, needs, chain, version),
|
||||
EncounterOrder.Trade => GetTrades(pk, needs, chain, version),
|
||||
EncounterOrder.Slot => GetSlots(pk, needs, chain, version),
|
||||
|
|
@ -274,11 +274,12 @@ private static IEnumerable<EncounterEgg> GetEggs(PKM pk, IReadOnlyCollection<int
|
|||
/// <param name="pk">Rough Pokémon data which contains the requested species, gender, and form.</param>
|
||||
/// <param name="needs">Moves which cannot be taught by the player.</param>
|
||||
/// <param name="chain">Origin possible evolution chain</param>
|
||||
/// <param name="version">Specific version to iterate for.</param>
|
||||
/// <returns>A consumable <see cref="IEncounterable"/> list of possible encounters.</returns>
|
||||
private static IEnumerable<MysteryGift> GetGifts(PKM pk, IReadOnlyCollection<int> needs, EvoCriteria[] chain)
|
||||
private static IEnumerable<MysteryGift> GetGifts(PKM pk, IReadOnlyCollection<int> needs, EvoCriteria[] chain, GameVersion version)
|
||||
{
|
||||
var format = pk.Format;
|
||||
var gifts = MysteryGiftGenerator.GetPossible(pk, chain);
|
||||
var gifts = MysteryGiftGenerator.GetPossible(pk, chain, version);
|
||||
foreach (var gift in gifts)
|
||||
{
|
||||
if (gift is WC3 {NotDistributed: true})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public static class EncounterEggGenerator
|
|||
{
|
||||
public static IEnumerable<EncounterEgg> GenerateEggs(PKM pkm, int generation, bool all = false)
|
||||
{
|
||||
var table = EvolutionTree.GetEvolutionTree(pkm, pkm.Format);
|
||||
var table = EvolutionTree.GetEvolutionTree(pkm.Context);
|
||||
int maxSpeciesOrigin = GetMaxSpeciesOrigin(generation);
|
||||
var evos = table.GetValidPreEvolutions(pkm, maxLevel: 100, maxSpeciesOrigin: maxSpeciesOrigin, skipChecks: true);
|
||||
return GenerateEggs(pkm, evos, generation, all);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ internal static class EncounterEggGenerator2
|
|||
{
|
||||
public static IEnumerable<EncounterEgg> GenerateEggs(PKM pkm, bool all = false)
|
||||
{
|
||||
var table = EvolutionTree.GetEvolutionTree(pkm, 2);
|
||||
var table = EvolutionTree.Evolves2;
|
||||
int maxSpeciesOrigin = Legal.GetMaxSpeciesOrigin(2);
|
||||
var evos = table.GetValidPreEvolutions(pkm, maxLevel: 100, maxSpeciesOrigin: maxSpeciesOrigin, skipChecks: true);
|
||||
return GenerateEggs(pkm, evos, all);
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ public static IEnumerable<EncounterSlot> GetPossible(PKM pkm, EvoCriteria[] chai
|
|||
|
||||
private static IEnumerable<EncounterSlot> GetRawEncounterSlots(PKM pkm, EvoCriteria[] chain, GameVersion gameSource)
|
||||
{
|
||||
if (!Locations.IsNoneLocation(gameSource, pkm.Egg_Location) || pkm.IsEgg)
|
||||
if (pkm.IsEgg)
|
||||
yield break;
|
||||
if (IsMetAsEgg(pkm))
|
||||
yield break;
|
||||
|
||||
var possibleAreas = GetEncounterAreas(pkm, gameSource);
|
||||
|
|
@ -62,6 +64,11 @@ public static IEnumerable<EncounterSlot> GetValidWildEncounters12(PKM pkm, EvoCr
|
|||
return GetRawEncounterSlots(pkm, chain, gameSource);
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterSlot> GetValidWildEncounters(PKM pkm, EvoCriteria[] chain, GameVersion gameSource)
|
||||
{
|
||||
return GetRawEncounterSlots(pkm, chain, gameSource);
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterSlot> GetValidWildEncounters(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
var gameSource = (GameVersion)pkm.Version;
|
||||
|
|
|
|||
|
|
@ -48,11 +48,6 @@ static IEnumerable<EncounterStatic> GetEvents(GameVersion g)
|
|||
return table.Where(e => chain.Any(d => d.Species == e.Species));
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterStatic> GetValidStaticEncounter(PKM pkm, EvoCriteria[] chain)
|
||||
{
|
||||
return GetValidStaticEncounter(pkm, chain, (GameVersion)pkm.Version);
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterStatic> GetValidStaticEncounter(PKM pkm, EvoCriteria[] chain, GameVersion gameSource)
|
||||
{
|
||||
var table = GetEncounterStaticTable(pkm, gameSource);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public static IEnumerable<EncounterTradeGB> GetValidEncounterTradesVC(PKM pkm, E
|
|||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, EvoCriteria[] chain)
|
||||
public static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, EvoCriteria[] chain, GameVersion game)
|
||||
{
|
||||
// Pre-filter for some language scenarios
|
||||
int lang = pkm.Language;
|
||||
|
|
@ -51,7 +51,6 @@ public static IEnumerable<EncounterTrade> GetValidEncounterTrades(PKM pkm, EvoCr
|
|||
if (lang == (int)LanguageID.Hacked && !EncounterTrade5PID.IsValidMissingLanguage(pkm)) // Japanese trades in BW have no language ID
|
||||
return Array.Empty<EncounterTrade>();
|
||||
|
||||
var game = (GameVersion)pkm.Version;
|
||||
var table = GetTable(game);
|
||||
return GetValidEncounterTrades(pkm, chain, table);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,20 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class MysteryGiftGenerator
|
||||
{
|
||||
public static IEnumerable<MysteryGift> GetPossible(PKM pkm, EvoCriteria[] chain)
|
||||
public static IEnumerable<MysteryGift> GetPossible(PKM pkm, EvoCriteria[] chain, GameVersion game)
|
||||
{
|
||||
// Ranger Manaphy is a PGT and is not in the PCD[] for gen4. Check manually.
|
||||
int gen = pkm.Generation;
|
||||
if (gen == 4 && pkm.Species == (int) Species.Manaphy)
|
||||
yield return RangerManaphy;
|
||||
|
||||
var table = GetTable(gen, pkm);
|
||||
var table = GetTable(gen, game);
|
||||
var possible = table.Where(wc => chain.Any(evo => evo.Species == wc.Species));
|
||||
foreach (var enc in possible)
|
||||
yield return enc;
|
||||
}
|
||||
|
||||
public static IEnumerable<MysteryGift> GetValidGifts(PKM pkm, EvoCriteria[] chain)
|
||||
public static IEnumerable<MysteryGift> GetValidGifts(PKM pkm, EvoCriteria[] chain, GameVersion game)
|
||||
{
|
||||
int gen = pkm.Generation;
|
||||
if (pkm.IsEgg && pkm.Format != gen) // transferred
|
||||
|
|
@ -28,18 +28,23 @@ public static IEnumerable<MysteryGift> GetValidGifts(PKM pkm, EvoCriteria[] chai
|
|||
|
||||
if (gen == 4) // check for Manaphy gift
|
||||
return GetMatchingPCD(pkm, MGDB_G4, chain);
|
||||
var table = GetTable(gen, pkm);
|
||||
var table = GetTable(gen, game);
|
||||
return GetMatchingGifts(pkm, table, chain);
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<MysteryGift> GetTable(int generation, PKM pkm) => generation switch
|
||||
private static IReadOnlyCollection<MysteryGift> GetTable(int generation, GameVersion game) => generation switch
|
||||
{
|
||||
3 => MGDB_G3,
|
||||
4 => MGDB_G4,
|
||||
5 => MGDB_G5,
|
||||
6 => MGDB_G6,
|
||||
7 => pkm.LGPE ? MGDB_G7GG : MGDB_G7,
|
||||
8 => pkm.BDSP ? MGDB_G8B : pkm.LA ? MGDB_G8A : MGDB_G8,
|
||||
7 => game is GameVersion.GP or GameVersion.GE ? MGDB_G7GG : MGDB_G7,
|
||||
8 => game switch
|
||||
{
|
||||
GameVersion.BD or GameVersion.SP => MGDB_G8B,
|
||||
GameVersion.PLA => MGDB_G8A,
|
||||
_ => MGDB_G8,
|
||||
},
|
||||
_ => Array.Empty<MysteryGift>(),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public static int GetLowestLevel(PKM pkm, byte startLevel)
|
|||
if (startLevel >= 100)
|
||||
startLevel = 100;
|
||||
|
||||
var table = EvolutionTree.GetEvolutionTree(pkm, pkm.Format);
|
||||
var table = EvolutionTree.GetEvolutionTree(pkm.Context);
|
||||
int count = 1;
|
||||
for (byte i = 100; i >= startLevel; i--)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public sealed class ValidEncounterMoves
|
|||
private const int EmptyCount = PKX.Generation + 1; // one for each generation index (and 0th)
|
||||
private static readonly IReadOnlyList<int>[] Empty = Enumerable.Repeat((IReadOnlyList<int>)new List<int>(), EmptyCount).ToArray();
|
||||
|
||||
public ValidEncounterMoves(PKM pkm, IEncounterTemplate encounter, EvoCriteria[][] chains)
|
||||
public ValidEncounterMoves(PKM pkm, IEncounterTemplate encounter, EvolutionHistory chains)
|
||||
{
|
||||
var level = MoveList.GetValidMovesAllGens(pkm, chains, types: MoveSourceType.Encounter, RemoveTransferHM: false);
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,9 @@ private static CheckResult VerifyEncounterEgg3Transfer(PKM pkm)
|
|||
return new CheckResult(Severity.Invalid, LTransferEgg, CheckIdentifier.Encounter);
|
||||
if (pkm.Met_Level < 5)
|
||||
return new CheckResult(Severity.Invalid, LTransferEggMetLevel, CheckIdentifier.Encounter);
|
||||
if (pkm.Egg_Location != 0)
|
||||
|
||||
var expectEgg = pkm is PB8 ? Locations.Default8bNone : 0;
|
||||
if (pkm.Egg_Location != expectEgg)
|
||||
return new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter);
|
||||
|
||||
if (pkm.Format != 4)
|
||||
|
|
@ -173,7 +175,7 @@ private static CheckResult VerifyEncounterEgg6(PKM pkm)
|
|||
if (pkm.AO)
|
||||
return VerifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_AO);
|
||||
|
||||
if (pkm.Egg_Location == 318)
|
||||
if (pkm.Egg_Location == Locations.HatchLocation6AO) // Battle Resort Daycare is only OR/AS.
|
||||
return new CheckResult(Severity.Invalid, LEggMetLocationFail, CheckIdentifier.Encounter);
|
||||
|
||||
return VerifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_XY);
|
||||
|
|
@ -193,7 +195,11 @@ private static CheckResult VerifyEncounterEgg7(PKM pkm)
|
|||
private static CheckResult VerifyEncounterEgg8(PKM pkm)
|
||||
{
|
||||
if (pkm.SWSH)
|
||||
{
|
||||
if (pkm.BDSP)
|
||||
return VerifyEncounterEggLevelLoc(pkm, 1, (location, game) => location == (game == GameVersion.SW ? Locations.HOME_SWBD : Locations.HOME_SHSP));
|
||||
return VerifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_SWSH);
|
||||
}
|
||||
|
||||
// no other games
|
||||
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
|
||||
|
|
@ -223,7 +229,7 @@ private static CheckResult VerifyEncounterEggLevelLoc(PKM pkm, int eggLevel, Fun
|
|||
: new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyUnhatchedEgg(PKM pkm, int tradeLoc, short noneLoc = 0)
|
||||
private static CheckResult VerifyUnhatchedEgg(PKM pkm, int tradeLoc, int noneLoc = 0)
|
||||
{
|
||||
var eggLevel = pkm.Format < 5 ? 0 : 1;
|
||||
if (pkm.Met_Level != eggLevel)
|
||||
|
|
@ -234,7 +240,7 @@ private static CheckResult VerifyUnhatchedEgg(PKM pkm, int tradeLoc, short noneL
|
|||
var met = pkm.Met_Location;
|
||||
if (met == tradeLoc)
|
||||
return new CheckResult(Severity.Valid, LEggLocationTrade, CheckIdentifier.Encounter);
|
||||
return (short)met == noneLoc
|
||||
return met == noneLoc
|
||||
? new CheckResult(Severity.Valid, LEggUnhatched, CheckIdentifier.Encounter)
|
||||
: new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ private static void ParseMovesWasEggPreRelearn(PKM pkm, CheckMoveResult[] parse,
|
|||
|
||||
var TradebackPreevo = pkm.Format == 2 && e.Species > 151;
|
||||
var NonTradebackLvlMoves = TradebackPreevo
|
||||
? MoveList.GetExclusivePreEvolutionMoves(pkm, e.Species, info.EvoChainsAllGens[2], 2, e.Version).Where(m => m > Legal.MaxMoveID_1).ToArray()
|
||||
? MoveList.GetExclusivePreEvolutionMoves(pkm, e.Species, info.EvoChainsAllGens.Gen2, 2, e.Version).Where(m => m > Legal.MaxMoveID_1).ToArray()
|
||||
: Array.Empty<int>();
|
||||
|
||||
var Egg = MoveEgg.GetEggMoves(pkm.PersonalInfo, e.Species, e.Form, e.Version, e.Generation);
|
||||
|
|
@ -238,9 +238,10 @@ private static void ParseMoves(PKM pkm, MoveParseSource source, LegalInfo info,
|
|||
// Special considerations!
|
||||
const int NoMinGeneration = 0;
|
||||
int minGeneration = NoMinGeneration;
|
||||
if (pkm is IBattleVersion {BattleVersion: not 0} v)
|
||||
if (pkm.IsOriginalMovesetDeleted())
|
||||
{
|
||||
minGeneration = ((GameVersion) v.BattleVersion).GetGeneration();
|
||||
var (_, resetGame) = pkm.IsMovesetRestricted();
|
||||
minGeneration = resetGame.GetGeneration();
|
||||
source.ResetSources();
|
||||
}
|
||||
|
||||
|
|
@ -578,7 +579,7 @@ private static void ParseEvolutionsIncompatibleMoves(PKM pkm, CheckMoveResult[]
|
|||
}
|
||||
}
|
||||
|
||||
private static void ParseShedinjaEvolveMoves(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves, EvoCriteria[][] evos)
|
||||
private static void ParseShedinjaEvolveMoves(PKM pkm, CheckMoveResult[] parse, IReadOnlyList<int> currentMoves, EvolutionHistory evos)
|
||||
{
|
||||
int shedinjaEvoMoveIndex = 0;
|
||||
var format = pkm.Format;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public static CheckMoveResult[] VerifyRelearn(PKM pkm, IEncounterTemplate enc, C
|
|||
};
|
||||
}
|
||||
|
||||
public static bool ShouldNotHaveRelearnMoves(IGeneration enc, PKM pkm) => enc.Generation < 6 || pkm is IBattleVersion {BattleVersion: not 0};
|
||||
public static bool ShouldNotHaveRelearnMoves(IGeneration enc, PKM pkm) => enc.Generation < 6 || pkm.IsOriginalMovesetDeleted();
|
||||
|
||||
private static CheckMoveResult[] VerifyRelearnSpecifiedMoveset(PKM pkm, IReadOnlyList<int> required, CheckMoveResult[] result)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class EvolutionChain
|
||||
{
|
||||
private static readonly EvoCriteria[] NONE = Array.Empty<EvoCriteria>();
|
||||
|
||||
internal static EvoCriteria[][] GetEvolutionChainsAllGens(PKM pkm, IEncounterTemplate enc)
|
||||
internal static EvolutionHistory GetEvolutionChainsAllGens(PKM pkm, IEncounterTemplate enc)
|
||||
{
|
||||
var chain = GetEvolutionChain(pkm, enc, pkm.Species, (byte)pkm.CurrentLevel);
|
||||
if (chain.Length == 0 || pkm.IsEgg || enc is EncounterInvalid)
|
||||
|
|
@ -17,40 +15,36 @@ internal static EvoCriteria[][] GetEvolutionChainsAllGens(PKM pkm, IEncounterTem
|
|||
return GetChainAll(pkm, enc, chain);
|
||||
}
|
||||
|
||||
private static EvoCriteria[][] GetAllEmpty(int count)
|
||||
private static EvolutionHistory GetChainSingle(PKM pkm, EvoCriteria[] fullChain)
|
||||
{
|
||||
var result = new EvoCriteria[count][];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = NONE; // default no-evolutions
|
||||
return result;
|
||||
var count = Math.Max(2, pkm.Format) + 1;
|
||||
return new EvolutionHistory(fullChain, count)
|
||||
{
|
||||
[pkm.Format] = fullChain,
|
||||
};
|
||||
}
|
||||
|
||||
private static EvoCriteria[][] GetChainSingle(PKM pkm, EvoCriteria[] fullChain)
|
||||
private static EvolutionHistory GetChainAll(PKM pkm, IEncounterTemplate enc, EvoCriteria[] fullChain)
|
||||
{
|
||||
var chain = GetAllEmpty(Math.Max(2, pkm.Format) + 1);
|
||||
chain[pkm.Format] = fullChain;
|
||||
return chain;
|
||||
}
|
||||
|
||||
private static EvoCriteria[][] GetChainAll(PKM pkm, IEncounterTemplate enc, EvoCriteria[] fullChain)
|
||||
{
|
||||
int maxgen = ParseSettings.AllowGen1Tradeback && pkm is PK1 ? 2 : pkm.Format;
|
||||
var GensEvoChains = GetAllEmpty(maxgen + 1);
|
||||
int maxgen = ParseSettings.AllowGen1Tradeback && pkm.Context == EntityContext.Gen1 ? 2 : pkm.Format;
|
||||
var GensEvoChains = new EvolutionHistory(fullChain, maxgen + 1);
|
||||
|
||||
var head = 0; // inlined FIFO queue indexing
|
||||
var mostEvolved = fullChain[head++];
|
||||
|
||||
var lvl = (byte)pkm.CurrentLevel;
|
||||
var maxLevel = lvl;
|
||||
int pkGen = enc.Generation;
|
||||
|
||||
// Iterate generations backwards
|
||||
// Maximum level of an earlier generation (GenX) will never be greater than a later generation (GenX+Y).
|
||||
int mingen = pkGen >= 3 ? pkGen : GBRestrictions.GetTradebackStatusInitial(pkm) == PotentialGBOrigin.Gen2Only ? 2 : 1;
|
||||
int mingen = enc.Generation;
|
||||
if (mingen is 1 or 2)
|
||||
mingen = GBRestrictions.GetTradebackStatusInitial(pkm) == PotentialGBOrigin.Gen2Only ? 2 : 1;
|
||||
|
||||
bool noxfrDecremented = true;
|
||||
for (int g = GensEvoChains.Length - 1; g >= mingen; g--)
|
||||
{
|
||||
if (pkGen <= 2 && g == 6)
|
||||
if (g == 6 && enc.Generation < 3)
|
||||
g = 2; // skip over 6543 as it never existed in these.
|
||||
|
||||
if (g <= 4 && pkm.Format > 2 && pkm.Format > g && !pkm.HasOriginalMetLocation)
|
||||
|
|
@ -70,99 +64,124 @@ private static EvoCriteria[][] GetChainAll(PKM pkm, IEncounterTemplate enc, EvoC
|
|||
if (head >= fullChain.Length)
|
||||
{
|
||||
if (g <= 2 && pkm.VC1)
|
||||
GensEvoChains[pkm.Format] = NONE; // invalidate here since we haven't reached the regular invalidation
|
||||
GensEvoChains.Invalidate(); // invalidate here since we haven't reached the regular invalidation
|
||||
return GensEvoChains;
|
||||
}
|
||||
if (mostEvolved.RequiresLvlUp)
|
||||
{
|
||||
// This is a Gen3 pokemon in a Gen4 phase evolution that requires level up and then transferred to Gen5+
|
||||
// We can deduce that it existed in Gen4 until met level,
|
||||
// but if current level is met level we can also deduce it existed in Gen3 until maximum met level -1
|
||||
if (g == 3 && pkm.Format > 4 && lvl == maxLevel)
|
||||
lvl--;
|
||||
ReviseMaxLevel(ref lvl, pkm, g, maxLevel);
|
||||
|
||||
// The same condition for Gen2 evolution of Gen1 pokemon, level of the pokemon in Gen1 games would be CurrentLevel -1 one level below Gen2 level
|
||||
else if (g == 1 && pkm.Format == 2 && lvl == maxLevel)
|
||||
lvl--;
|
||||
}
|
||||
mostEvolved = fullChain[head++];
|
||||
}
|
||||
|
||||
// Alolan form evolutions, remove from gens 1-6 chains
|
||||
if (HasAlolanForm(mostEvolved.Species))
|
||||
if (g < 7 && HasAlolanForm(mostEvolved.Species) && pkm.Format >= 7 && mostEvolved.Form > 0)
|
||||
{
|
||||
if (g < 7 && pkm.Format >= 7 && mostEvolved.Form > 0)
|
||||
{
|
||||
if (head >= fullChain.Length)
|
||||
break;
|
||||
mostEvolved = fullChain[head++];
|
||||
}
|
||||
if (head >= fullChain.Length)
|
||||
return GensEvoChains;
|
||||
mostEvolved = fullChain[head++];
|
||||
}
|
||||
|
||||
GensEvoChains[g] = GetEvolutionChain(pkm, enc, mostEvolved.Species, lvl);
|
||||
ref var genChain = ref GensEvoChains[g];
|
||||
if (genChain.Length == 0)
|
||||
var tmp = GetEvolutionChain(pkm, enc, mostEvolved.Species, lvl);
|
||||
if (tmp.Length == 0)
|
||||
continue;
|
||||
|
||||
if (g > 2 && !pkm.HasOriginalMetLocation && g >= pkGen && noxfrDecremented)
|
||||
GensEvoChains[g] = tmp;
|
||||
if (g == 1)
|
||||
{
|
||||
bool isTransferred = HasMetLocationUpdatedTransfer(pkGen, g);
|
||||
CleanGen1(pkm, enc, GensEvoChains);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g >= 3 && !pkm.HasOriginalMetLocation && g >= enc.Generation && noxfrDecremented)
|
||||
{
|
||||
bool isTransferred = HasMetLocationUpdatedTransfer(enc.Generation, g);
|
||||
if (!isTransferred)
|
||||
continue;
|
||||
|
||||
noxfrDecremented = g > (pkGen != 3 ? 4 : 5);
|
||||
noxfrDecremented = g > (enc.Generation != 3 ? 4 : 5);
|
||||
|
||||
// Remove previous evolutions below transfer level
|
||||
// For example a gen3 Charizard in format 7 with current level 36 and met level 36, thus could never be Charmander / Charmeleon in Gen5+.
|
||||
// chain level for Charmander is 35, is below met level.
|
||||
int minlvl = GetMinLevelGeneration(pkm, g);
|
||||
|
||||
ref var genChain = ref GensEvoChains[g];
|
||||
int minIndex = Array.FindIndex(genChain, e => e.LevelMax >= minlvl);
|
||||
if (minIndex != -1)
|
||||
genChain = genChain.AsSpan(minIndex).ToArray();
|
||||
}
|
||||
else if (g == 1)
|
||||
{
|
||||
// Remove Gen7 pre-evolutions and chain break scenarios
|
||||
if (pkm.VC1)
|
||||
TrimVC1Transfer(pkm, GensEvoChains);
|
||||
|
||||
ref var lastGen = ref GensEvoChains[1];
|
||||
var g1 = lastGen.AsSpan();
|
||||
// Remove Gen2 post-evolutions (Scizor, Blissey...)
|
||||
if (g1[0].Species > MaxSpeciesID_1)
|
||||
{
|
||||
if (g1.Length == 1)
|
||||
{
|
||||
lastGen = Array.Empty<EvoCriteria>();
|
||||
continue; // done
|
||||
}
|
||||
g1 = g1[1..];
|
||||
}
|
||||
|
||||
// Remove Gen2 pre-evolutions (Pichu, Cleffa...)
|
||||
if (g1[^1].Species > MaxSpeciesID_1)
|
||||
{
|
||||
if (g1.Length == 1)
|
||||
{
|
||||
lastGen = Array.Empty<EvoCriteria>();
|
||||
continue; // done
|
||||
}
|
||||
g1 = g1[..^1];
|
||||
}
|
||||
|
||||
if (g1.Length != lastGen.Length)
|
||||
lastGen = g1.ToArray();
|
||||
// Update min level for the encounter to prevent certain level up moves.
|
||||
if (g1.Length != 0)
|
||||
{
|
||||
ref var last = ref g1[^1];
|
||||
last = last with { LevelMin = enc.LevelMin };
|
||||
}
|
||||
}
|
||||
}
|
||||
return GensEvoChains;
|
||||
}
|
||||
|
||||
private static void ReviseMaxLevel(ref byte lvl, PKM pkm, int g, byte maxLevel)
|
||||
{
|
||||
// This is a Gen3 pokemon in a Gen4 phase evolution that requires level up and then transferred to Gen5+
|
||||
// We can deduce that it existed in Gen4 until met level,
|
||||
// but if current level is met level we can also deduce it existed in Gen3 until maximum met level -1
|
||||
if (g == 3 && pkm.Format > 4 && lvl == maxLevel)
|
||||
lvl--;
|
||||
|
||||
// The same condition for Gen2 evolution of Gen1 pokemon, level of the pokemon in Gen1 games would be CurrentLevel -1 one level below Gen2 level
|
||||
else if (g == 1 && pkm.Format == 2 && lvl == maxLevel)
|
||||
lvl--;
|
||||
}
|
||||
|
||||
private static void CleanGen1(PKM pkm, IEncounterTemplate enc, EvolutionHistory chains)
|
||||
{
|
||||
// Remove Gen7 pre-evolutions and chain break scenarios
|
||||
if (pkm.VC1)
|
||||
{
|
||||
var index = Array.FindLastIndex(chains.Gen7, z => z.Species <= MaxSpeciesID_1);
|
||||
if (index == -1)
|
||||
{
|
||||
chains.Invalidate(); // needed a Gen1 species present; invalidate the chain.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TrimSpeciesAbove(enc, MaxSpeciesID_1, ref chains.Gen1);
|
||||
}
|
||||
|
||||
private static void TrimSpeciesAbove(IEncounterTemplate enc, int species, ref EvoCriteria[] chain)
|
||||
{
|
||||
var span = chain.AsSpan();
|
||||
|
||||
// Remove post-evolutions
|
||||
if (span[0].Species > species)
|
||||
{
|
||||
if (span.Length == 1)
|
||||
{
|
||||
chain = Array.Empty<EvoCriteria>();
|
||||
return;
|
||||
}
|
||||
|
||||
span = span[1..];
|
||||
}
|
||||
|
||||
// Remove pre-evolutions
|
||||
if (span[^1].Species > species)
|
||||
{
|
||||
if (span.Length == 1)
|
||||
{
|
||||
chain = Array.Empty<EvoCriteria>();
|
||||
return;
|
||||
}
|
||||
|
||||
span = span[..^1];
|
||||
}
|
||||
|
||||
if (span.Length != chain.Length)
|
||||
chain = span.ToArray();
|
||||
|
||||
// Update min level for the encounter to prevent certain level up moves.
|
||||
if (span.Length != 0)
|
||||
{
|
||||
ref var last = ref span[^1];
|
||||
last = last with { LevelMin = enc.LevelMin };
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HasMetLocationUpdatedTransfer(int originalGeneration, int currentGeneration) => originalGeneration switch
|
||||
{
|
||||
< 3 => currentGeneration >= 3,
|
||||
|
|
@ -170,29 +189,20 @@ private static EvoCriteria[][] GetChainAll(PKM pkm, IEncounterTemplate enc, EvoC
|
|||
_ => false,
|
||||
};
|
||||
|
||||
private static void TrimVC1Transfer(PKM pkm, EvoCriteria[][] allChains)
|
||||
{
|
||||
var vc7 = allChains[7];
|
||||
var gen1Index = Array.FindLastIndex(vc7, z => z.Species <= MaxSpeciesID_1);
|
||||
if (gen1Index == -1)
|
||||
allChains[pkm.Format] = NONE; // needed a Gen1 species present; invalidate the chain.
|
||||
}
|
||||
|
||||
private static EvoCriteria[] GetEvolutionChain(PKM pkm, IEncounterTemplate enc, int mostEvolvedSpecies, byte maxlevel)
|
||||
{
|
||||
int min = enc.LevelMin;
|
||||
if (pkm.HasOriginalMetLocation && pkm.Met_Level != 0)
|
||||
min = pkm.Met_Level;
|
||||
|
||||
var chain = GetValidPreEvolutions(pkm, minLevel: min);
|
||||
return TrimChain(chain, enc, mostEvolvedSpecies, maxlevel);
|
||||
}
|
||||
|
||||
private static EvoCriteria[] TrimChain(EvoCriteria[] chain, IEncounterTemplate enc, int mostEvolvedSpecies, byte maxlevel)
|
||||
{
|
||||
if (enc.Species == mostEvolvedSpecies)
|
||||
{
|
||||
if (chain.Length == 1)
|
||||
return chain;
|
||||
var index = Array.FindLastIndex(chain, z => z.Species == enc.Species);
|
||||
if (index == -1)
|
||||
return Array.Empty<EvoCriteria>();
|
||||
return new[] { chain[index] };
|
||||
}
|
||||
return TrimChainSingle(chain, enc);
|
||||
|
||||
// Evolution chain is in reverse order (devolution)
|
||||
// Find the index of the minimum species to determine the end of the chain
|
||||
|
|
@ -210,6 +220,11 @@ private static EvoCriteria[] GetEvolutionChain(PKM pkm, IEncounterTemplate enc,
|
|||
CheckLastEncounterRemoval(enc, chain);
|
||||
}
|
||||
|
||||
return TrimChainMore(chain, mostEvolvedSpecies, maxlevel);
|
||||
}
|
||||
|
||||
private static EvoCriteria[] TrimChainMore(EvoCriteria[] chain, int mostEvolvedSpecies, byte maxlevel)
|
||||
{
|
||||
// maxspec is used to remove future geneneration evolutions, to gather evolution chain of a pokemon in previous generations
|
||||
var maxSpeciesIndex = Array.FindIndex(chain, z => z.Species == mostEvolvedSpecies);
|
||||
if (maxSpeciesIndex > 0)
|
||||
|
|
@ -223,13 +238,28 @@ private static EvoCriteria[] GetEvolutionChain(PKM pkm, IEncounterTemplate enc,
|
|||
chain = Array.FindAll(chain, z => z.LevelMin <= maxlevel);
|
||||
|
||||
// Reduce the evolution chain levels to max level to limit any later analysis/results.
|
||||
SanitizeMaxLevel(chain, maxlevel);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
private static void SanitizeMaxLevel(EvoCriteria[] chain, byte maxlevel)
|
||||
{
|
||||
for (var i = 0; i < chain.Length; i++)
|
||||
{
|
||||
ref var c = ref chain[i];
|
||||
c = c with { LevelMax = Math.Min(c.LevelMax, maxlevel) };
|
||||
}
|
||||
}
|
||||
|
||||
return chain;
|
||||
private static EvoCriteria[] TrimChainSingle(EvoCriteria[] chain, IEncounterTemplate enc)
|
||||
{
|
||||
if (chain.Length == 1)
|
||||
return chain;
|
||||
var index = Array.FindLastIndex(chain, z => z.Species == enc.Species);
|
||||
if (index == -1)
|
||||
return Array.Empty<EvoCriteria>();
|
||||
return new[] { chain[index] };
|
||||
}
|
||||
|
||||
private static void CheckLastEncounterRemoval(IEncounterTemplate enc, EvoCriteria[] chain)
|
||||
|
|
@ -263,8 +293,10 @@ internal static EvoCriteria[] GetValidPreEvolutions(PKM pkm, int maxspeciesorigi
|
|||
if (maxspeciesorigin == -1 && pkm.InhabitedGeneration(2) && pkm.Format <= 2 && pkm.Generation == 1)
|
||||
maxspeciesorigin = MaxSpeciesID_2;
|
||||
|
||||
int tree = Math.Max(2, pkm.Format);
|
||||
var et = EvolutionTree.GetEvolutionTree(pkm, tree);
|
||||
var context = pkm.Context;
|
||||
if (context < EntityContext.Gen2)
|
||||
context = EntityContext.Gen2;
|
||||
var et = EvolutionTree.GetEvolutionTree(context);
|
||||
return et.GetValidPreEvolutions(pkm, maxLevel: (byte)maxLevel, maxSpeciesOrigin: maxspeciesorigin, skipChecks: skipChecks, minLevel: (byte)minLevel);
|
||||
}
|
||||
|
||||
|
|
|
|||
55
PKHeX.Core/Legality/Evolutions/EvolutionHistory.cs
Normal file
55
PKHeX.Core/Legality/Evolutions/EvolutionHistory.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the possible evolution bounds for a parsed entity with respect to its origins and game traversal.
|
||||
/// </summary>
|
||||
public class EvolutionHistory
|
||||
{
|
||||
private static readonly EvoCriteria[] NONE = Array.Empty<EvoCriteria>();
|
||||
|
||||
public EvoCriteria[] Gen1 = NONE;
|
||||
public EvoCriteria[] Gen2 = NONE;
|
||||
public EvoCriteria[] Gen3 = NONE;
|
||||
public EvoCriteria[] Gen4 = NONE;
|
||||
public EvoCriteria[] Gen5 = NONE;
|
||||
public EvoCriteria[] Gen6 = NONE;
|
||||
public EvoCriteria[] Gen7 = NONE;
|
||||
public EvoCriteria[] Gen7b = NONE;
|
||||
public EvoCriteria[] Gen8 = NONE;
|
||||
|
||||
public EvoCriteria[] Gen8a => Gen8; // future: separate field instead of copy
|
||||
public EvoCriteria[] Gen8b => Gen8; // future: separate field instead of copy
|
||||
|
||||
public readonly int Length;
|
||||
public readonly EvoCriteria[] FullChain;
|
||||
|
||||
public EvolutionHistory(EvoCriteria[] fullChain, int count)
|
||||
{
|
||||
FullChain = fullChain;
|
||||
Length = count;
|
||||
}
|
||||
|
||||
public ref EvoCriteria[] this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (index == 1) return ref Gen1;
|
||||
if (index == 2) return ref Gen2;
|
||||
if (index == 3) return ref Gen3;
|
||||
if (index == 4) return ref Gen4;
|
||||
if (index == 5) return ref Gen5;
|
||||
if (index == 6) return ref Gen6;
|
||||
if (index == 7) return ref Gen7;
|
||||
if (index == 8) return ref Gen8;
|
||||
throw new IndexOutOfRangeException(nameof(index));
|
||||
}
|
||||
}
|
||||
|
||||
internal void Invalidate() => this[Length - 1] = NONE;
|
||||
|
||||
public bool HasVisitedSWSH => Gen8.Length != 0;
|
||||
public bool HasVisitedPLA => Gen8a.Length != 0;
|
||||
public bool HasVisitedBDSP => Gen8b.Length != 0;
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@ internal static class EvolutionLegality
|
|||
(int)Species.PorygonZ,
|
||||
|
||||
(int)Species.Sylveon,
|
||||
|
||||
(int)Species.Kleavor,
|
||||
};
|
||||
|
||||
private static readonly HashSet<int> FutureEvolutionsGen2 = new()
|
||||
|
|
@ -53,6 +55,9 @@ internal static class EvolutionLegality
|
|||
(int)Species.PorygonZ,
|
||||
|
||||
(int)Species.Sylveon,
|
||||
|
||||
(int)Species.Wyrdeer,
|
||||
(int)Species.Ursaluna,
|
||||
};
|
||||
|
||||
private static readonly HashSet<int> FutureEvolutionsGen3 = new()
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ namespace PKHeX.Core
|
|||
/// </remarks>
|
||||
public sealed class EvolutionTree
|
||||
{
|
||||
private static readonly EvolutionTree Evolves1 = new(GetResource("rby"), Gen1, PersonalTable.Y, MaxSpeciesID_1);
|
||||
private static readonly EvolutionTree Evolves2 = new(GetResource("gsc"), Gen2, PersonalTable.C, MaxSpeciesID_2);
|
||||
private static readonly EvolutionTree Evolves3 = new(GetResource("g3"), Gen3, PersonalTable.RS, MaxSpeciesID_3);
|
||||
private static readonly EvolutionTree Evolves4 = new(GetResource("g4"), Gen4, PersonalTable.DP, MaxSpeciesID_4);
|
||||
private static readonly EvolutionTree Evolves5 = new(GetResource("g5"), Gen5, PersonalTable.BW, MaxSpeciesID_5);
|
||||
private static readonly EvolutionTree Evolves6 = new(GetReader("ao"), Gen6, PersonalTable.AO, MaxSpeciesID_6);
|
||||
private static readonly EvolutionTree Evolves7 = new(GetReader("uu"), Gen7, PersonalTable.USUM, MaxSpeciesID_7_USUM);
|
||||
private static readonly EvolutionTree Evolves7b = new(GetReader("gg"), Gen7, PersonalTable.GG, MaxSpeciesID_7b);
|
||||
private static readonly EvolutionTree Evolves8 = new(GetReader("ss"), Gen8, PersonalTable.SWSH, MaxSpeciesID_8);
|
||||
private static readonly EvolutionTree Evolves8a = new(GetReader("la"), Gen8, PersonalTable.LA, MaxSpeciesID_8a);
|
||||
private static readonly EvolutionTree Evolves8b = new(GetReader("bs"), Gen8, PersonalTable.BDSP, MaxSpeciesID_8b);
|
||||
public static readonly EvolutionTree Evolves1 = new(GetResource("rby"), Gen1, PersonalTable.Y, MaxSpeciesID_1);
|
||||
public static readonly EvolutionTree Evolves2 = new(GetResource("gsc"), Gen2, PersonalTable.C, MaxSpeciesID_2);
|
||||
public static readonly EvolutionTree Evolves3 = new(GetResource("g3"), Gen3, PersonalTable.RS, MaxSpeciesID_3);
|
||||
public static readonly EvolutionTree Evolves4 = new(GetResource("g4"), Gen4, PersonalTable.DP, MaxSpeciesID_4);
|
||||
public static readonly EvolutionTree Evolves5 = new(GetResource("g5"), Gen5, PersonalTable.BW, MaxSpeciesID_5);
|
||||
public static readonly EvolutionTree Evolves6 = new(GetReader("ao"), Gen6, PersonalTable.AO, MaxSpeciesID_6);
|
||||
public static readonly EvolutionTree Evolves7 = new(GetReader("uu"), Gen7, PersonalTable.USUM, MaxSpeciesID_7_USUM);
|
||||
public static readonly EvolutionTree Evolves7b = new(GetReader("gg"), Gen7, PersonalTable.GG, MaxSpeciesID_7b);
|
||||
public static readonly EvolutionTree Evolves8 = new(GetReader("ss"), Gen8, PersonalTable.SWSH, MaxSpeciesID_8);
|
||||
public static readonly EvolutionTree Evolves8a = new(GetReader("la"), Gen8, PersonalTable.LA, MaxSpeciesID_8a);
|
||||
public static readonly EvolutionTree Evolves8b = new(GetReader("bs"), Gen8, PersonalTable.BDSP, MaxSpeciesID_8b);
|
||||
|
||||
private static ReadOnlySpan<byte> GetResource(string resource) => Util.GetBinaryResource($"evos_{resource}.pkl");
|
||||
private static BinLinkerAccessor GetReader(string resource) => BinLinkerAccessor.Get(GetResource(resource), resource);
|
||||
|
|
@ -38,33 +38,20 @@ static EvolutionTree()
|
|||
Evolves8b.FixEvoTreeBS();
|
||||
}
|
||||
|
||||
public static EvolutionTree GetEvolutionTree(int generation) => generation switch
|
||||
public static EvolutionTree GetEvolutionTree(EntityContext context) => context switch
|
||||
{
|
||||
1 => Evolves1,
|
||||
2 => Evolves2,
|
||||
3 => Evolves3,
|
||||
4 => Evolves4,
|
||||
5 => Evolves5,
|
||||
6 => Evolves6,
|
||||
7 => Evolves7,
|
||||
_ => Evolves8,
|
||||
};
|
||||
|
||||
public static EvolutionTree GetEvolutionTree(PKM pkm, int generation) => generation switch
|
||||
{
|
||||
1 => Evolves1,
|
||||
2 => Evolves2,
|
||||
3 => Evolves3,
|
||||
4 => Evolves4,
|
||||
5 => Evolves5,
|
||||
6 => Evolves6,
|
||||
7 => pkm.Version is (int)GO or (int)GP or (int)GE ? Evolves7b : Evolves7,
|
||||
_ => pkm.Version switch
|
||||
{
|
||||
(int)PLA => Evolves8a,
|
||||
(int)BD or (int)SP => Evolves8b,
|
||||
_ => Evolves8,
|
||||
},
|
||||
EntityContext.Gen1 => Evolves1,
|
||||
EntityContext.Gen2 => Evolves2,
|
||||
EntityContext.Gen3 => Evolves3,
|
||||
EntityContext.Gen4 => Evolves4,
|
||||
EntityContext.Gen5 => Evolves5,
|
||||
EntityContext.Gen6 => Evolves6,
|
||||
EntityContext.Gen7 => Evolves7,
|
||||
EntityContext.Gen8 => Evolves8,
|
||||
EntityContext.Gen7b => Evolves7b,
|
||||
EntityContext.Gen8a => Evolves8a,
|
||||
EntityContext.Gen8b => Evolves8b,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(context), context, null)
|
||||
};
|
||||
|
||||
private readonly IReadOnlyList<EvolutionMethod[]> Entries;
|
||||
|
|
@ -233,26 +220,11 @@ public EvoCriteria[] GetValidPreEvolutions(PKM pkm, byte maxLevel, int maxSpecie
|
|||
{
|
||||
if (maxSpeciesOrigin <= 0)
|
||||
maxSpeciesOrigin = GetMaxSpeciesOrigin(pkm);
|
||||
if (pkm.IsEgg && !skipChecks)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new EvoCriteria{ Species = (ushort)pkm.Species, Form = (byte)pkm.Form, LevelMax = maxLevel, LevelMin = maxLevel },
|
||||
};
|
||||
}
|
||||
|
||||
// Shedinja's evolution case can be a little tricky; hard-code handling.
|
||||
if (pkm.Species == (int)Species.Shedinja && maxLevel >= 20 && (!pkm.HasOriginalMetLocation || minLevel < maxLevel))
|
||||
{
|
||||
var min = Math.Max(minLevel, (byte)20);
|
||||
return new[]
|
||||
{
|
||||
new EvoCriteria { Species = (ushort)Species.Shedinja, LevelMax = maxLevel, LevelMin = min, Method = EvolutionType.LevelUp },
|
||||
new EvoCriteria { Species = (ushort)Species.Nincada, LevelMax = maxLevel, LevelMin = minLevel },
|
||||
};
|
||||
}
|
||||
ushort species = (ushort)pkm.Species;
|
||||
byte form = (byte)pkm.Form;
|
||||
|
||||
return GetExplicitLineage(pkm, maxLevel, skipChecks, maxSpeciesOrigin, minLevel);
|
||||
return GetExplicitLineage(species, form, pkm, minLevel, maxLevel, maxSpeciesOrigin, skipChecks);
|
||||
}
|
||||
|
||||
public bool IsSpeciesDerivedFrom(int species, int form, int otherSpecies, int otherForm, bool ignoreForm = true)
|
||||
|
|
@ -348,16 +320,39 @@ public IEnumerable<int> GetEvolutions(int species, int form)
|
|||
/// <summary>
|
||||
/// Generates the reverse evolution path for the input <see cref="pkm"/>.
|
||||
/// </summary>
|
||||
/// <param name="species">Entity Species to begin the chain</param>
|
||||
/// <param name="form">Entity Form to begin the chain</param>
|
||||
/// <param name="pkm">Entity data</param>
|
||||
/// <param name="maxLevel">Maximum level</param>
|
||||
/// <param name="levelMin">Minimum level</param>
|
||||
/// <param name="levelMax">Maximum level</param>
|
||||
/// <param name="maxSpeciesID">Clamp for maximum species ID</param>
|
||||
/// <param name="skipChecks">Skip the secondary checks that validate the evolution</param>
|
||||
/// <param name="maxSpeciesOrigin">Clamp for maximum species ID</param>
|
||||
/// <param name="minLevel">Minimum level</param>
|
||||
private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks, int maxSpeciesOrigin, byte minLevel)
|
||||
private EvoCriteria[] GetExplicitLineage(ushort species, byte form, PKM pkm, byte levelMin, byte levelMax, int maxSpeciesID, bool skipChecks)
|
||||
{
|
||||
var species = pkm.Species;
|
||||
var form = pkm.Form;
|
||||
var lvl = maxLevel;
|
||||
if (pkm.IsEgg && !skipChecks)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new EvoCriteria{ Species = species, Form = form, LevelMax = levelMax, LevelMin = levelMax },
|
||||
};
|
||||
}
|
||||
|
||||
// Shedinja's evolution case can be a little tricky; hard-code handling.
|
||||
if (species == (int)Species.Shedinja && levelMax >= 20 && (!pkm.HasOriginalMetLocation || levelMin < levelMax))
|
||||
{
|
||||
var min = Math.Max(levelMin, (byte)20);
|
||||
return new[]
|
||||
{
|
||||
new EvoCriteria { Species = (ushort)Species.Shedinja, LevelMax = levelMax, LevelMin = min, Method = EvolutionType.LevelUp },
|
||||
new EvoCriteria { Species = (ushort)Species.Nincada, LevelMax = levelMax, LevelMin = levelMin },
|
||||
};
|
||||
}
|
||||
return GetLineage(species, form, pkm, levelMin, levelMax, maxSpeciesID, skipChecks);
|
||||
}
|
||||
|
||||
private EvoCriteria[] GetLineage(int species, int form, PKM pkm, byte levelMin, byte levelMax, int maxSpeciesID, bool skipChecks)
|
||||
{
|
||||
var lvl = levelMax;
|
||||
var first = new EvoCriteria { Species = (ushort)species, Form = (byte)form, LevelMax = lvl };
|
||||
|
||||
const int maxEvolutions = 3;
|
||||
|
|
@ -366,7 +361,8 @@ private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks
|
|||
|
||||
switch (species)
|
||||
{
|
||||
case (int)Species.Silvally: form = 0;
|
||||
case (int)Species.Silvally:
|
||||
form = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -376,9 +372,9 @@ private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks
|
|||
while (true)
|
||||
{
|
||||
var key = GetLookupKey(species, form);
|
||||
bool oneValid = false;
|
||||
var node = Lineage[key];
|
||||
|
||||
bool oneValid = false;
|
||||
foreach (var link in node)
|
||||
{
|
||||
if (link.IsEvolutionBanned(pkm) && !skipChecks)
|
||||
|
|
@ -388,17 +384,18 @@ private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks
|
|||
if (!evo.Valid(pkm, lvl, skipChecks))
|
||||
continue;
|
||||
|
||||
if (evo.RequiresLevelUp && minLevel >= lvl)
|
||||
if (evo.RequiresLevelUp && levelMin >= lvl)
|
||||
break; // impossible evolution
|
||||
|
||||
oneValid = true;
|
||||
UpdateMinValues(evos[..ctr], evo, minLevel);
|
||||
UpdateMinValues(evos[..ctr], evo, levelMin);
|
||||
|
||||
species = link.Species;
|
||||
form = link.Form;
|
||||
evos[ctr++] = evo.GetEvoCriteria((ushort)species, (byte)form, lvl);
|
||||
if (evo.RequiresLevelUp)
|
||||
lvl--;
|
||||
|
||||
oneValid = true;
|
||||
break;
|
||||
}
|
||||
if (!oneValid)
|
||||
|
|
@ -407,11 +404,11 @@ private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks
|
|||
|
||||
// Remove future gen pre-evolutions; no Munchlax from a Gen3 Snorlax, no Pichu from a Gen1-only Raichu, etc
|
||||
ref var last = ref evos[ctr - 1];
|
||||
if (last.Species > maxSpeciesOrigin)
|
||||
if (last.Species > maxSpeciesID)
|
||||
{
|
||||
for (int i = 0; i < ctr; i++)
|
||||
{
|
||||
if (evos[i].Species > maxSpeciesOrigin)
|
||||
if (evos[i].Species > maxSpeciesID)
|
||||
continue;
|
||||
ctr--;
|
||||
break;
|
||||
|
|
@ -421,9 +418,16 @@ private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks
|
|||
// Last species is the wild/hatched species, the minimum level is because it has not evolved from previous species
|
||||
var result = evos[..ctr];
|
||||
last = ref result[^1];
|
||||
last = last with { LevelMin = minLevel, LevelUpRequired = 0 };
|
||||
last = last with { LevelMin = levelMin, LevelUpRequired = 0 };
|
||||
|
||||
// Rectify minimum levels
|
||||
RectifyMinimumLevels(result);
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static void RectifyMinimumLevels(Span<EvoCriteria> result)
|
||||
{
|
||||
for (int i = result.Length - 2; i >= 0; i--)
|
||||
{
|
||||
ref var evo = ref result[i];
|
||||
|
|
@ -431,8 +435,6 @@ private EvoCriteria[] GetExplicitLineage(PKM pkm, byte maxLevel, bool skipChecks
|
|||
var min = (byte)Math.Max(prev.LevelMin + evo.LevelUpRequired, evo.LevelMin);
|
||||
evo = evo with { LevelMin = min };
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static void UpdateMinValues(Span<EvoCriteria> evos, EvolutionMethod evo, byte minLevel)
|
||||
|
|
|
|||
|
|
@ -467,6 +467,7 @@ public static class LegalityCheckStrings
|
|||
public static string LTransferFlagIllegal { get; set; } = "Flagged as illegal by the game (glitch abuse).";
|
||||
public static string LTransferHTFlagRequired { get; set; } = "Current handler cannot be past gen OT for transferred specimen.";
|
||||
public static string LTransferMet { get; set; } = "Invalid Met Location, expected Poké Transfer or Crown.";
|
||||
public static string LTransferNotPossible { get; set; } = "Unable to transfer into current format from origin format.";
|
||||
public static string LTransferMetLocation { get; set; } = "Invalid Transfer Met Location.";
|
||||
public static string LTransferMove { get; set; } = "Incompatible transfer move.";
|
||||
public static string LTransferMoveG4HM { get; set; } = "Defog and Whirlpool. One of the two moves should have been removed before transferred to Generation 5.";
|
||||
|
|
|
|||
|
|
@ -192,7 +192,11 @@ public void SetEvolutionMoves(Span<int> moves, int ctr = 0)
|
|||
public void SetLevelUpMoves(int startLevel, int endLevel, Span<int> moves, ReadOnlySpan<int> ignore, int ctr = 0)
|
||||
{
|
||||
int startIndex = Array.FindIndex(Levels, z => z >= startLevel);
|
||||
if (startIndex == -1)
|
||||
return; // No more remain
|
||||
int endIndex = Array.FindIndex(Levels, z => z > endLevel);
|
||||
if (endIndex == -1)
|
||||
endIndex = Levels.Length;
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
int move = Moves[i];
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ private void UpdateVCTransferInfo()
|
|||
var enc = (Info.EncounterOriginalGB = EncounterMatch);
|
||||
if (enc is EncounterInvalid)
|
||||
return;
|
||||
var vc = EncounterStaticGenerator.GetVCStaticTransferEncounter(pkm, enc, Info.EvoChainsAllGens[7]);
|
||||
var vc = EncounterStaticGenerator.GetVCStaticTransferEncounter(pkm, enc, Info.EvoChainsAllGens.Gen7);
|
||||
Info.EncounterMatch = vc;
|
||||
|
||||
foreach (var z in Transfer.VerifyVCEncounter(pkm, enc, vc, Info.Moves))
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ internal static int[] GetBaseEggMoves(PKM pkm, int species, int form, GameVersio
|
|||
return Array.Empty<int>();
|
||||
}
|
||||
|
||||
internal static IReadOnlyList<int>[] GetValidMovesAllGens(PKM pkm, EvoCriteria[][] evoChains, MoveSourceType types = MoveSourceType.ExternalSources, bool RemoveTransferHM = true)
|
||||
internal static IReadOnlyList<int>[] GetValidMovesAllGens(PKM pkm, EvolutionHistory evoChains, MoveSourceType types = MoveSourceType.ExternalSources, bool RemoveTransferHM = true)
|
||||
{
|
||||
var result = new IReadOnlyList<int>[evoChains.Length];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
|
|
@ -181,9 +181,7 @@ internal static IReadOnlyList<int>[] GetValidMovesAllGens(PKM pkm, EvoCriteria[]
|
|||
|
||||
internal static IEnumerable<int> GetValidMoves(PKM pkm, EvoCriteria[] evoChain, int generation, MoveSourceType types = MoveSourceType.ExternalSources, bool RemoveTransferHM = true)
|
||||
{
|
||||
GameVersion version = (GameVersion)pkm.Version;
|
||||
if (!pkm.IsMovesetRestricted(generation))
|
||||
version = Any;
|
||||
var (_, version) = pkm.IsMovesetRestricted();
|
||||
return GetValidMoves(pkm, version, evoChain, generation, types: types, RemoveTransferHM: RemoveTransferHM);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
public static class MoveListSuggest
|
||||
{
|
||||
private static int[] GetSuggestedMoves(PKM pkm, EvoCriteria[][] evoChains, MoveSourceType types, IEncounterTemplate enc)
|
||||
private static int[] GetSuggestedMoves(PKM pkm, EvolutionHistory evoChains, MoveSourceType types, IEncounterTemplate enc)
|
||||
{
|
||||
if (pkm.IsEgg && pkm.Format <= 5) // pre relearn
|
||||
return MoveList.GetBaseEggMoves(pkm, pkm.Species, 0, (GameVersion)pkm.Version, pkm.CurrentLevel);
|
||||
|
|
@ -30,15 +30,13 @@ private static int[] GetSuggestedMoves(PKM pkm, EvoCriteria[][] evoChains, MoveS
|
|||
return GetValidMoves(pkm, evoChains, types).Skip(1).ToArray(); // skip move 0
|
||||
}
|
||||
|
||||
private static IEnumerable<int> GetValidMoves(PKM pkm, EvoCriteria[][] evoChains, MoveSourceType types = MoveSourceType.ExternalSources, bool RemoveTransferHM = true)
|
||||
private static IEnumerable<int> GetValidMoves(PKM pkm, EvolutionHistory evoChains, MoveSourceType types = MoveSourceType.ExternalSources, bool RemoveTransferHM = true)
|
||||
{
|
||||
GameVersion version = (GameVersion)pkm.Version;
|
||||
if (!pkm.IsMovesetRestricted())
|
||||
version = GameVersion.Any;
|
||||
var (_, version) = pkm.IsMovesetRestricted();
|
||||
return GetValidMoves(pkm, version, evoChains, types: types, RemoveTransferHM: RemoveTransferHM);
|
||||
}
|
||||
|
||||
private static IEnumerable<int> GetValidMoves(PKM pkm, GameVersion version, EvoCriteria[][] evoChains, MoveSourceType types = MoveSourceType.Reminder, bool RemoveTransferHM = true)
|
||||
private static IEnumerable<int> GetValidMoves(PKM pkm, GameVersion version, EvolutionHistory evoChains, MoveSourceType types = MoveSourceType.Reminder, bool RemoveTransferHM = true)
|
||||
{
|
||||
var r = new List<int> { 0 };
|
||||
if (types.HasFlagFast(MoveSourceType.RelearnMoves) && pkm.Format >= 6)
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ private static readonly LearnLookup
|
|||
|
||||
public static LearnVersion GetIsLevelUpMove(PKM pkm, int species, int form, int maxLevel, int generation, int move, int minlvlG1, int minlvlG2, GameVersion version = Any)
|
||||
{
|
||||
if (pkm.IsMovesetRestricted(generation))
|
||||
version = (GameVersion)pkm.Version;
|
||||
var restrict = pkm.IsMovesetRestricted();
|
||||
if (restrict.IsRestricted)
|
||||
version = restrict.Game;
|
||||
|
||||
return generation switch
|
||||
{
|
||||
|
|
@ -43,7 +44,7 @@ public static LearnVersion GetIsLevelUpMove(PKM pkm, int species, int form, int
|
|||
4 => GetIsLevelUp4(species, form, move, maxLevel, version),
|
||||
5 => GetIsLevelUp5(species, form, move, maxLevel, version),
|
||||
6 => GetIsLevelUp6(species, form, move, maxLevel, version),
|
||||
7 => GetIsLevelUp7(species, form, move, version), // move reminder can give any move 1-100
|
||||
7 => GetIsLevelUp7(species, form, move, pkm.GG ? (GameVersion)pkm.Version : version), // move reminder can give any move 1-100
|
||||
8 => GetIsLevelUp8(species, form, move, maxLevel, version),
|
||||
_ => LearnNONE,
|
||||
};
|
||||
|
|
@ -267,8 +268,10 @@ private static LearnVersion GetIsLevelUp3Deoxys(int form, int move, int lvl, Gam
|
|||
|
||||
public static IEnumerable<int> GetMovesLevelUp(PKM pkm, int species, int form, int maxLevel, int minlvlG1, int minlvlG2, GameVersion version, bool MoveReminder, int generation)
|
||||
{
|
||||
if (pkm.IsMovesetRestricted(generation))
|
||||
version = (GameVersion)pkm.Version;
|
||||
var restrict = pkm.IsMovesetRestricted();
|
||||
if (restrict.IsRestricted)
|
||||
version = restrict.Game;
|
||||
|
||||
return generation switch
|
||||
{
|
||||
1 => GetMovesLevelUp1(species, form, maxLevel, minlvlG1, version),
|
||||
|
|
@ -277,7 +280,7 @@ public static IEnumerable<int> GetMovesLevelUp(PKM pkm, int species, int form, i
|
|||
4 => GetMovesLevelUp4(species, form, maxLevel, version),
|
||||
5 => GetMovesLevelUp5(species, form, maxLevel, version),
|
||||
6 => GetMovesLevelUp6(species, form, maxLevel, version),
|
||||
7 => GetMovesLevelUp7(species, form, maxLevel, MoveReminder, version),
|
||||
7 => GetMovesLevelUp7(species, form, maxLevel, MoveReminder, pkm.GG ? (GameVersion)pkm.Version : version),
|
||||
8 => GetMovesLevelUp8(species, form, maxLevel, version),
|
||||
_ => Array.Empty<int>(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ public static class MoveTechnicalMachine
|
|||
{
|
||||
public static GameVersion GetIsMachineMove(PKM pkm, int species, int form, int generation, int move, GameVersion ver = GameVersion.Any, bool RemoveTransfer = false)
|
||||
{
|
||||
if (pkm.IsMovesetRestricted(generation))
|
||||
ver = (GameVersion) pkm.Version;
|
||||
var (isRestricted, game) = pkm.IsMovesetRestricted();
|
||||
if (isRestricted)
|
||||
ver = game;
|
||||
|
||||
switch (generation)
|
||||
{
|
||||
case 1: return GetIsMachine1(species, move);
|
||||
|
|
@ -30,8 +32,10 @@ public static GameVersion GetIsMachineMove(PKM pkm, int species, int form, int g
|
|||
|
||||
public static GameVersion GetIsRecordMove(PKM pkm, int species, int form, int generation, int move, GameVersion ver = GameVersion.Any, bool allowBit = false)
|
||||
{
|
||||
if (pkm.IsMovesetRestricted(generation))
|
||||
ver = (GameVersion)pkm.Version;
|
||||
var (isRestricted, game) = pkm.IsMovesetRestricted();
|
||||
if (isRestricted)
|
||||
ver = game;
|
||||
|
||||
return generation switch
|
||||
{
|
||||
8 => GetIsRecord8(pkm, species, move, form, ver, allowBit),
|
||||
|
|
@ -223,8 +227,9 @@ private static GameVersion GetIsRecord8(PKM pkm, int species, int move, int form
|
|||
public static IEnumerable<int> GetTMHM(PKM pkm, int species, int form, int generation, GameVersion ver = GameVersion.Any, bool RemoveTransfer = true)
|
||||
{
|
||||
var r = new List<int>();
|
||||
if (pkm.IsMovesetRestricted(generation))
|
||||
ver = (GameVersion)pkm.Version;
|
||||
var (isRestricted, game) = pkm.IsMovesetRestricted();
|
||||
if (isRestricted)
|
||||
ver = game;
|
||||
|
||||
switch (generation)
|
||||
{
|
||||
|
|
@ -237,7 +242,7 @@ public static IEnumerable<int> GetTMHM(PKM pkm, int species, int form, int gener
|
|||
case 4: AddMachine4(r, species, pkm.Format, RemoveTransfer, form); break;
|
||||
case 5: AddMachine5(r, species, form); break;
|
||||
case 6: AddMachine6(r, species, form, ver); break;
|
||||
case 7: AddMachine7(r, species, form, ver); break;
|
||||
case 7: AddMachine7(r, species, form, pkm.GG ? (GameVersion)pkm.Version : ver); break;
|
||||
case 8: AddMachine8(r, species, form, ver); break;
|
||||
}
|
||||
return r.Distinct();
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ private static GameVersion GetIsTutor7(PKM pkm, int species, int form, bool spec
|
|||
|
||||
private static GameVersion GetIsTutor8(PKM pkm, int species, int form, bool specialTutors, int move)
|
||||
{
|
||||
if (pkm.LA)
|
||||
if (pkm is PA8)
|
||||
{
|
||||
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(species, form);
|
||||
if (!pi.IsPresentInGame)
|
||||
|
|
@ -278,7 +278,7 @@ private static void AddMovesTutor7(List<int> moves, int species, int form, PKM p
|
|||
|
||||
private static void AddMovesTutor8(List<int> moves, int species, int form, PKM pkm, bool specialTutors)
|
||||
{
|
||||
if (pkm.LA)
|
||||
if (pkm is PA8)
|
||||
{
|
||||
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(species, form);
|
||||
if (!pi.IsPresentInGame)
|
||||
|
|
@ -292,7 +292,7 @@ private static void AddMovesTutor8(List<int> moves, int species, int form, PKM p
|
|||
}
|
||||
return;
|
||||
}
|
||||
if (pkm.BDSP)
|
||||
if (pkm is PB8)
|
||||
{
|
||||
var pi = (PersonalInfoBDSP)PersonalTable.BDSP.GetFormEntry(species, form);
|
||||
if (!pi.IsPresentInGame)
|
||||
|
|
|
|||
|
|
@ -19,16 +19,18 @@ internal static class EvolutionRestrictions
|
|||
/// </summary>
|
||||
private static readonly Dictionary<int, MoveEvolution> SpeciesEvolutionWithMove = new()
|
||||
{
|
||||
{(int)Eevee, new(0, 0)}, // FairyMoves
|
||||
{(int)MimeJr, new(1, (int)Mimic)},
|
||||
{(int)Bonsly, new(2, (int)Mimic)},
|
||||
{(int)Aipom, new(3, (int)Mimic)},
|
||||
{(int)Lickitung, new(4, (int)Rollout)},
|
||||
{(int)Tangela, new(5, (int)AncientPower)},
|
||||
{(int)Yanma, new(6, (int)AncientPower)},
|
||||
{(int)Piloswine, new(7, (int)AncientPower)},
|
||||
{(int)Steenee, new(8, (int)Stomp)},
|
||||
{(int)Clobbopus, new(9, (int)Taunt)},
|
||||
{(int)Eevee, new(00, 0)}, // FairyMoves
|
||||
{(int)MimeJr, new(01, (int)Mimic)},
|
||||
{(int)Bonsly, new(02, (int)Mimic)},
|
||||
{(int)Aipom, new(03, (int)Mimic)},
|
||||
{(int)Lickitung, new(04, (int)Rollout)},
|
||||
{(int)Tangela, new(05, (int)AncientPower)},
|
||||
{(int)Yanma, new(06, (int)AncientPower)},
|
||||
{(int)Piloswine, new(07, (int)AncientPower)},
|
||||
{(int)Steenee, new(08, (int)Stomp)},
|
||||
{(int)Clobbopus, new(09, (int)Taunt)},
|
||||
{(int)Stantler, new(10, (int)PsyshieldBash)},
|
||||
{(int)Qwilfish, new(11, (int)BarbBarrage)},
|
||||
};
|
||||
|
||||
private readonly record struct MoveEvolution(int ReferenceIndex, int Move);
|
||||
|
|
@ -65,6 +67,7 @@ internal static class EvolutionRestrictions
|
|||
(int)SpiritBreak,
|
||||
(int)StrangeSteam,
|
||||
(int)MistyExplosion,
|
||||
(int)SpringtideStorm,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -85,7 +88,6 @@ internal static class EvolutionRestrictions
|
|||
new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 35 }, // Grapploct (Clobbopus with Taunt)
|
||||
new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00 }, // Wyrdeer (Stantler with AGILE Psyshield Bash)
|
||||
new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00 }, // Overqwil (Qwilfish with STRONG Barb Barrage)
|
||||
new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00 }, // Basculegion (Basculin-2 with Recoil Move)
|
||||
};
|
||||
|
||||
private static readonly byte[] MinLevelEvolutionWithMove_8LA =
|
||||
|
|
@ -102,7 +104,6 @@ internal static class EvolutionRestrictions
|
|||
99, // Grapploct (Clobbopus with Taunt)
|
||||
31, // Wyrdeer (Stantler with AGILE Psyshield Bash)
|
||||
25, // Overqwil (Qwilfish with STRONG Barb Barrage)
|
||||
34, // Basculegion (Basculin-2 with Recoil Move)
|
||||
};
|
||||
|
||||
private static readonly bool[][] CanEggHatchWithEvolveMove =
|
||||
|
|
@ -119,7 +120,6 @@ internal static class EvolutionRestrictions
|
|||
new [] { false, false, false, false, false, false, false, false, true }, // Grapploct (Clobbopus with Taunt)
|
||||
new [] { false, false, false, false, false, false, false, false, false }, // Wyrdeer (Stantler with AGILE Psyshield Bash)
|
||||
new [] { false, false, false, false, false, false, false, false, false }, // Overqwil (Qwilfish with STRONG Barb Barrage)
|
||||
new [] { false, false, false, false, false, false, false, false, false }, // Basculegion (Basculin-2 with Recoil Move)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -170,13 +170,13 @@ public static bool IsValidEvolutionWithMove(PKM pkm, LegalInfo info)
|
|||
}
|
||||
|
||||
// Current level must be at least the minimum post-evolution level.
|
||||
var lvl = GetMinLevelKnowRequiredMove(pkm, gen, index);
|
||||
var lvl = GetMinLevelKnowRequiredMove(pkm, gen, index, info.EvoChainsAllGens);
|
||||
return pkm.CurrentLevel >= lvl;
|
||||
}
|
||||
|
||||
private static int GetMinLevelKnowRequiredMove(PKM pkm, int gen, int index)
|
||||
private static int GetMinLevelKnowRequiredMove(PKM pkm, int gen, int index, EvolutionHistory evos)
|
||||
{
|
||||
if (gen == 8 && pkm.LA) // No Level Up required, and different levels than mainline SW/SH.
|
||||
if (gen == 8 && pkm.HasVisitedLA(evos.Gen8a)) // No Level Up required, and different levels than mainline SW/SH.
|
||||
return MinLevelEvolutionWithMove_8LA[index];
|
||||
|
||||
var lvl = GetLevelLearnMove(pkm, gen, index);
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ internal static IEnumerable<GameVersion> GetGen1Versions(IEncounterTemplate enc)
|
|||
private static bool GetCatchRateMatchesPreEvolution(PK1 pkm, int catch_rate)
|
||||
{
|
||||
// For species catch rate, discard any species that has no valid encounters and a different catch rate than their pre-evolutions
|
||||
var table = EvolutionTree.GetEvolutionTree(1);
|
||||
var table = EvolutionTree.Evolves1;
|
||||
var chain = table.GetValidPreEvolutions(pkm, maxLevel: (byte)pkm.CurrentLevel);
|
||||
foreach (var entry in chain)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public override IEnumerable<ushort> GetMemoryItemParams() => Legal.HeldItem_AO.C
|
|||
public override bool CanObtainMemoryOT(GameVersion pkmVersion, byte memory) => pkmVersion switch
|
||||
{
|
||||
GameVersion.SW or GameVersion.SH => CanObtainMemorySWSH(memory),
|
||||
_ => false,
|
||||
_ => memory is 0,
|
||||
};
|
||||
|
||||
public override bool CanObtainMemoryHT(GameVersion pkmVersion, byte memory) => CanObtainMemorySWSH(memory);
|
||||
|
|
|
|||
|
|
@ -66,12 +66,6 @@ public static bool CanBuyItem(int generation, int item, GameVersion version = Ga
|
|||
return context.CanBuyItem(item, version);
|
||||
}
|
||||
|
||||
public static bool GetCanLearnMachineMove(PKM pkm, int move, int generation, GameVersion version = GameVersion.Any)
|
||||
{
|
||||
var evos = EvolutionChain.GetValidPreEvolutions(pkm);
|
||||
return GetCanLearnMachineMove(pkm, evos, move, generation, version);
|
||||
}
|
||||
|
||||
public static bool GetCanLearnMachineMove(PKM pkm, EvoCriteria[] evos, int move, int generation, GameVersion version = GameVersion.Any)
|
||||
{
|
||||
if (IsOtherFormMove(pkm, evos, move, generation, version, types: MoveSourceType.AllMachines))
|
||||
|
|
@ -145,10 +139,10 @@ public static bool CanKnowMove(PKM pkm, MemoryVariableSet memory, int gen, Legal
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool IsSpecialEncounterMoveEggDeleted(PKM pkm, IEncounterable enc)
|
||||
private static bool IsSpecialEncounterMoveEggDeleted(PKM pkm, IEncounterTemplate enc)
|
||||
{
|
||||
if (pkm is IBattleVersion { BattleVersion: not 0 }) // can hide Relearn moves (Gen6+ Eggs, or DexNav)
|
||||
return enc is EncounterEgg { Generation: >= 6 } or EncounterSlot6AO { CanDexNav: true } or EncounterSlot8b { IsUnderground: true };
|
||||
if (pkm.IsOriginalMovesetDeleted())
|
||||
return true;
|
||||
return enc is EncounterEgg { Generation: < 6 }; // egg moves that are no longer in the movepool
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +153,7 @@ public static bool GetCanRelearnMove(PKM pkm, int move, int generation, EvoCrite
|
|||
return MoveList.GetValidMoves(pkm, version, evos, generation, types: MoveSourceType.Reminder).Contains(move);
|
||||
}
|
||||
|
||||
private static bool GetCanKnowMove(PKM pkm, int move, int generation, EvoCriteria[][] evos, GameVersion version = GameVersion.Any)
|
||||
private static bool GetCanKnowMove(PKM pkm, int move, int generation, EvolutionHistory evos, GameVersion version = GameVersion.Any)
|
||||
{
|
||||
if (pkm.Species == (int)Smeargle)
|
||||
return Legal.IsValidSketch(move, generation);
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ private static CheckMoveResult[] GetArray()
|
|||
|
||||
private static readonly ValidEncounterMoves NONE = new();
|
||||
public ValidEncounterMoves EncounterMoves { get; internal set; } = NONE;
|
||||
public EvoCriteria[][] EvoChainsAllGens => _evochains ??= EvolutionChain.GetEvolutionChainsAllGens(pkm, EncounterMatch);
|
||||
private EvoCriteria[][]? _evochains;
|
||||
public EvolutionHistory EvoChainsAllGens => _evochains ??= EvolutionChain.GetEvolutionChainsAllGens(pkm, EncounterMatch);
|
||||
private EvolutionHistory? _evochains;
|
||||
|
||||
/// <summary><see cref="RNG"/> related information that generated the <see cref="PKM.PID"/>/<see cref="PKM.IVs"/> value(s).</summary>
|
||||
public PIDIV PIDIV
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ public static bool IsValidSketch(int move, int generation)
|
|||
return false;
|
||||
if (generation is 8) // can't Sketch unusable moves in BDSP, no Sketch in PLA
|
||||
{
|
||||
if (SignatureSketch_BDSP.Contains(move) || DummiedMoves_BDSP.Contains(move))
|
||||
if (DummiedMoves_BDSP.Contains(move))
|
||||
return false;
|
||||
if (move > MaxMoveID_8)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ public static partial class Legal
|
|||
internal const int MaxSpeciesID_8b = MaxSpeciesID_4; // Arceus-493
|
||||
internal const int MaxMoveID_8b = MaxMoveID_8_R2;
|
||||
internal const int MaxItemID_8b = 1822; // DS Sounds
|
||||
internal const int MaxBallID_8b = (int)Ball.Beast;
|
||||
internal const int MaxBallID_8b = (int)Ball.LAOrigin;
|
||||
internal const int MaxGameID_8b = (int)GameVersion.SP;
|
||||
internal const int MaxAbilityID_8b = MaxAbilityID_8_R2;
|
||||
|
||||
|
|
@ -119,14 +119,6 @@ public static partial class Legal
|
|||
(int)Move.DracoMeteor,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Unavailable Sketch Moves that are only learnable once certain species are distributed / made accessible.
|
||||
/// </summary>
|
||||
public static readonly HashSet<int> SignatureSketch_BDSP = new()
|
||||
{
|
||||
(int)Move.PsychoBoost, // Deoxys
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Moves that are kill
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ private CheckResult VerifyAbility(LegalityAnalysis data)
|
|||
if (abilities[0] == abilities[1] && num != 1)
|
||||
{
|
||||
// Check if any pre-evolution could have it flipped.
|
||||
var evos = data.Info.EvoChainsAllGens[6];
|
||||
var pt = GameData.GetPersonal(GameUtil.GetVersion(format));
|
||||
var evos = data.Info.EvoChainsAllGens.Gen6;
|
||||
var pt = GameData.GetPersonal(pkm.Context.GetSingleGameVersion());
|
||||
if (!GetWasDual(evos, pt, pkm))
|
||||
return INVALID;
|
||||
}
|
||||
|
|
@ -68,7 +68,8 @@ private CheckResult VerifyAbility(LegalityAnalysis data)
|
|||
|
||||
if (format >= 8) // Ability Patch
|
||||
{
|
||||
if (pkm.AbilityNumber == 4 && !pkm.LA)
|
||||
var evos = data.Info.EvoChainsAllGens;
|
||||
if (pkm.AbilityNumber == 4 && IsAccessibleAbilityPatch(pkm, evos))
|
||||
{
|
||||
if (CanAbilityPatch(format, abilities, pkm.Species))
|
||||
return GetValid(LAbilityPatchUsed);
|
||||
|
|
@ -163,7 +164,7 @@ private CheckResult VerifyFixedAbility(LegalityAnalysis data, IReadOnlyList<int>
|
|||
var enc = data.Info.EncounterMatch;
|
||||
if (enc.Generation >= 6)
|
||||
{
|
||||
if (IsAbilityCapsuleModified(pkm, abilities, encounterAbility))
|
||||
if (IsAbilityCapsuleModified(pkm, abilities, encounterAbility, data.Info.EvoChainsAllGens))
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
if (pkm.AbilityNumber != 1 << encounterAbility.GetSingleValue())
|
||||
return INVALID;
|
||||
|
|
@ -202,7 +203,7 @@ private CheckResult VerifyFixedAbility(LegalityAnalysis data, IReadOnlyList<int>
|
|||
if (state == AbilityState.CanMismatch || encounterAbility == 0)
|
||||
return CheckMatch(pkm, abilities, enc.Generation, AbilityState.MustMatch, enc);
|
||||
|
||||
if (IsAbilityCapsuleModified(pkm, abilities, encounterAbility))
|
||||
if (IsAbilityCapsuleModified(pkm, abilities, encounterAbility, data.Info.EvoChainsAllGens))
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
|
||||
return INVALID;
|
||||
|
|
@ -231,7 +232,7 @@ private AbilityState VerifyAbilityPreCapsule(LegalityAnalysis data, IReadOnlyLis
|
|||
return AbilityState.MustMatch;
|
||||
|
||||
// If the species could not exist in Gen3, must match.
|
||||
var g3 = info.EvoChainsAllGens[3];
|
||||
var g3 = info.EvoChainsAllGens.Gen3;
|
||||
if (g3.Length == 0)
|
||||
return AbilityState.MustMatch;
|
||||
|
||||
|
|
@ -251,7 +252,7 @@ private AbilityState VerifyAbilityGen3Transfer(LegalityAnalysis data, IReadOnlyL
|
|||
return AbilityState.MustMatch;
|
||||
|
||||
var chain = data.Info.EvoChainsAllGens;
|
||||
bool evolved45 = chain[4].Length > 1 || (pkm.Format == 5 && chain[5].Length > 1);
|
||||
bool evolved45 = chain.Gen4.Length > 1 || (pkm.Format == 5 && chain.Gen5.Length > 1);
|
||||
if (evolved45)
|
||||
{
|
||||
if (pkm.Ability == pers.Ability1) // Could evolve in Gen4/5 and have a Gen3 only ability
|
||||
|
|
@ -445,15 +446,27 @@ private CheckResult GetPIDAbilityMatch(PKM pkm, IReadOnlyList<int> abilities)
|
|||
return VALID;
|
||||
}
|
||||
|
||||
// Ability Capsule can change between 1/2
|
||||
private static bool IsAbilityCapsuleModified(PKM pkm, IReadOnlyList<int> abilities, AbilityPermission encounterAbility)
|
||||
private static bool IsAccessibleAbilityPatch(PKM pkm, EvolutionHistory evosAll)
|
||||
{
|
||||
return pkm.HasVisitedSWSH(evosAll.Gen8) || pkm.HasVisitedBDSP(evosAll.Gen8b);
|
||||
}
|
||||
|
||||
private static bool IsAccessibleAbilityCapsule(PKM pkm, EvolutionHistory evosAll)
|
||||
{
|
||||
if (evosAll.Gen6.Length > 0 || evosAll.Gen7.Length > 0)
|
||||
return true;
|
||||
return pkm.HasVisitedSWSH(evosAll.Gen8) || pkm.HasVisitedBDSP(evosAll.Gen8b);
|
||||
}
|
||||
|
||||
// Ability Capsule can change between 1/2
|
||||
private static bool IsAbilityCapsuleModified(PKM pkm, IReadOnlyList<int> abilities, AbilityPermission encounterAbility, EvolutionHistory evos)
|
||||
{
|
||||
if (!IsAccessibleAbilityCapsule(pkm, evos))
|
||||
return false; // Not available.
|
||||
if (!CanAbilityCapsule(pkm.Format, abilities))
|
||||
return false;
|
||||
if (pkm.AbilityNumber == 4)
|
||||
return false; // Cannot alter to hidden ability.
|
||||
if (pkm.LA)
|
||||
return false; // Not available.
|
||||
if (encounterAbility == AbilityPermission.OnlyHidden)
|
||||
return false; // Cannot alter from hidden ability.
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -984,60 +984,6 @@ internal static class BallBreedLegality
|
|||
(int)Flabébé + (3 << 11), // Flabébé-Blue
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// All egg species that can inherit a Safari Ball when bred in BD/SP.
|
||||
/// </summary>
|
||||
internal static readonly HashSet<int> InheritSafari_BDSP = new()
|
||||
{
|
||||
(int)Ekans,
|
||||
(int)Azurill,
|
||||
(int)Barboach,
|
||||
(int)Bidoof,
|
||||
(int)Budew,
|
||||
(int)Carnivine,
|
||||
(int)Carvanha,
|
||||
(int)Croagunk,
|
||||
(int)Exeggcute,
|
||||
(int)Gulpin,
|
||||
(int)Hoothoot,
|
||||
(int)Kangaskhan,
|
||||
(int)Magikarp,
|
||||
(int)Marill,
|
||||
(int)Paras,
|
||||
(int)Psyduck,
|
||||
(int)Roselia,
|
||||
(int)Shroomish,
|
||||
(int)Skorupi,
|
||||
(int)Starly,
|
||||
(int)Wooper,
|
||||
(int)Yanma,
|
||||
};
|
||||
|
||||
internal static readonly HashSet<int> BanInheritedExceptSafari_BDSP = new()
|
||||
{
|
||||
(int)Exeggcute,
|
||||
(int)Kangaskhan,
|
||||
(int)Yanma,
|
||||
(int)Shroomish,
|
||||
(int)Gulpin,
|
||||
(int)Carnivine,
|
||||
};
|
||||
|
||||
internal static readonly HashSet<int> BanInheritedBall_BDSP = new()
|
||||
{
|
||||
// Gen1 Fossils
|
||||
(int)Aerodactyl, (int)Omanyte, (int)Kabuto,
|
||||
// Gen3 Fossils
|
||||
(int)Lileep, (int)Anorith,
|
||||
// Gen4 Fossils
|
||||
(int)Cranidos, (int)Shieldon,
|
||||
|
||||
// Riolu Egg from Riley
|
||||
(int)Riolu,
|
||||
|
||||
(int)Phione,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a legal <see cref="Ball"/> value for a bred egg encounter.
|
||||
/// </summary>
|
||||
|
|
@ -1045,14 +991,9 @@ internal static class BallBreedLegality
|
|||
/// <param name="species">Species the egg contained.</param>
|
||||
/// <returns>Valid ball to hatch with.</returns>
|
||||
/// <remarks>Not all things can hatch with a Poké Ball!</remarks>
|
||||
#pragma warning disable RCS1163, IDE0060 // Unused parameter.
|
||||
public static Ball GetDefaultBall(GameVersion version, int species)
|
||||
{
|
||||
if (version is GameVersion.BD or GameVersion.SP)
|
||||
{
|
||||
if (BanInheritedExceptSafari_BDSP.Contains(species))
|
||||
return Ball.Safari;
|
||||
}
|
||||
|
||||
return Ball.None;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,21 @@ public override void Verify(LegalityAnalysis data)
|
|||
data.AddLine(result);
|
||||
}
|
||||
|
||||
private static int IsReplacedBall(IVersion enc, PKM pk) => pk switch
|
||||
{
|
||||
PK8 when enc.Version == GameVersion.PLA => (int)Poke,
|
||||
_ => (int)None,
|
||||
};
|
||||
|
||||
private CheckResult VerifyBall(LegalityAnalysis data)
|
||||
{
|
||||
var Info = data.Info;
|
||||
var enc = Info.EncounterMatch;
|
||||
|
||||
var ball = IsReplacedBall(enc, data.pkm);
|
||||
if (ball != 0)
|
||||
return VerifyBallEquals(data, ball);
|
||||
|
||||
// Fixed ball cases -- can be only one ball ever
|
||||
switch (enc)
|
||||
{
|
||||
|
|
@ -49,7 +59,7 @@ private CheckResult VerifyBall(LegalityAnalysis data)
|
|||
// The special move verifier has a similar check!
|
||||
if (pkm.HGSS && pkm.Ball == (int)Sport) // Can evolve in DP to retain the HG/SS ball -- not able to be captured in any other ball
|
||||
return VerifyBallEquals(data, (int)Sport);
|
||||
if (Info.Generation != 3 || Info.EvoChainsAllGens[3].Length != 2)
|
||||
if (Info.Generation != 3 || Info.EvoChainsAllGens.Gen3.Length != 2)
|
||||
return VerifyBallEquals(data, (int)Poke); // Pokeball Only
|
||||
}
|
||||
|
||||
|
|
@ -264,20 +274,81 @@ private CheckResult VerifyBallEggGen7(LegalityAnalysis data)
|
|||
private CheckResult VerifyBallEggGen8BDSP(LegalityAnalysis data)
|
||||
{
|
||||
int species = data.EncounterMatch.Species;
|
||||
if (BallBreedLegality.BanInheritedBall_BDSP.Contains(species))
|
||||
if (species == (int)Species.Phione)
|
||||
return VerifyBallEquals(data, (int)Poke);
|
||||
if (BallBreedLegality.BanInheritedExceptSafari_BDSP.Contains(species))
|
||||
return VerifyBallEquals(data, (int)Safari);
|
||||
|
||||
if (data.pkm.Ball == (int)Safari)
|
||||
if (species is (int)Species.Cranidos or (int)Species.Shieldon)
|
||||
return VerifyBallEquals(data, BallUseLegality.DreamWorldBalls);
|
||||
|
||||
var pkm = data.pkm;
|
||||
Ball ball = (Ball)pkm.Ball;
|
||||
var balls = BallUseLegality.GetWildBalls(8, GameVersion.BDSP);
|
||||
if (balls.Contains((int)ball))
|
||||
return GetValid(LBallSpeciesPass);
|
||||
|
||||
if (species is (int)Species.Spinda)
|
||||
return GetInvalid(LBallSpecies); // Can't enter or exit, needs to adhere to wild balls.
|
||||
|
||||
// Cross-game inheritance
|
||||
if (IsGalarCatchAndBreed(species))
|
||||
{
|
||||
if (BallBreedLegality.InheritSafari_BDSP.Contains(species))
|
||||
if (BallUseLegality.WildPokeballs8.Contains(pkm.Ball))
|
||||
return GetValid(LBallSpeciesPass);
|
||||
}
|
||||
|
||||
if (ball == Safari)
|
||||
{
|
||||
if (!(BallBreedLegality.Inherit_Safari.Contains(species) || BallBreedLegality.Inherit_SafariMale.Contains(species)))
|
||||
return GetInvalid(LBallSpecies);
|
||||
if (BallBreedLegality.Ban_SafariBallHidden_7.Contains(species) && IsHiddenAndNotPossible(pkm))
|
||||
return GetInvalid(LBallAbility);
|
||||
return GetValid(LBallSpeciesPass);
|
||||
}
|
||||
if (ball.IsApricornBall()) // Apricorn Ball
|
||||
{
|
||||
if (!BallBreedLegality.Inherit_Apricorn7.Contains(species))
|
||||
return GetInvalid(LBallSpecies);
|
||||
if (BallBreedLegality.Ban_NoHidden8Apricorn.Contains(species | pkm.Form << 11) && IsHiddenAndNotPossible(pkm)) // lineage is 3->2->origin
|
||||
return GetInvalid(LBallAbility);
|
||||
return GetValid(LBallSpeciesPass);
|
||||
}
|
||||
if (ball == Sport) // Sport Ball
|
||||
{
|
||||
if (!BallBreedLegality.Inherit_Sport.Contains(species))
|
||||
return GetInvalid(LBallSpecies);
|
||||
if ((species is (int)Species.Volbeat or (int)Species.Illumise) && IsHiddenAndNotPossible(pkm)) // Volbeat/Illumise
|
||||
return GetInvalid(LBallAbility);
|
||||
return GetValid(LBallSpeciesPass);
|
||||
}
|
||||
if (ball == Dream) // Dream Ball
|
||||
{
|
||||
if (BallBreedLegality.Inherit_Dream.Contains(species) || BallBreedLegality.Inherit_DreamMale.Contains(species))
|
||||
return GetValid(LBallSpeciesPass);
|
||||
return GetInvalid(LBallSpecies);
|
||||
}
|
||||
if (ball is >= Dusk and <= Quick) // Dusk Heal Quick
|
||||
{
|
||||
if (!BallBreedLegality.Ban_Gen4Ball_7.Contains(species))
|
||||
return GetValid(LBallSpeciesPass);
|
||||
return GetInvalid(LBallSpecies);
|
||||
}
|
||||
if (ball is >= Ultra and <= Premier) // Don't worry, Safari was already checked.
|
||||
{
|
||||
if (!BallBreedLegality.Ban_Gen3Ball_7.Contains(species))
|
||||
return GetValid(LBallSpeciesPass);
|
||||
return GetInvalid(LBallSpecies);
|
||||
}
|
||||
|
||||
var balls = BallUseLegality.GetWildBalls(8, GameVersion.BDSP);
|
||||
return VerifyBallEquals(data, balls);
|
||||
if (ball == Beast)
|
||||
{
|
||||
if (BallBreedLegality.PastGenAlolanScans.Contains(species))
|
||||
return GetValid(LBallSpeciesPass);
|
||||
}
|
||||
|
||||
if (ball > Beast)
|
||||
return GetInvalid(LBallUnavailable);
|
||||
|
||||
return GetInvalid(LBallEncMismatch);
|
||||
}
|
||||
|
||||
private CheckResult VerifyBallEggGen8(LegalityAnalysis data)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
var pkm = data.pkm;
|
||||
if (data.EncounterMatch is EncounterStatic3 s3)
|
||||
VerifyCXDStarterCorrelation(data, s3);
|
||||
else if (pkm.Egg_Location != 0) // can't obtain eggs in CXD
|
||||
else if (pkm.Egg_Location != 0 && pkm is not PB8 {Egg_Location: Locations.Default8bNone}) // can't obtain eggs in CXD
|
||||
data.AddLine(GetInvalid(LEncInvalid, CheckIdentifier.Encounter)); // invalid encounter
|
||||
|
||||
if (pkm.OT_Gender == 1)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
// In generations 3,4 and BDSP, blocks/poffins have a feel(sheen) equal to sheen=sum(stats)/5, with +/- 10% for a favored stat.
|
||||
// In generation 6 (ORAS), they don't award any sheen, so any value is legal.
|
||||
|
||||
var correlation = GetContestStatRestriction(pkm, data.Info.Generation);
|
||||
var correlation = GetContestStatRestriction(pkm, data.Info.Generation, data.Info.EvoChainsAllGens);
|
||||
if (correlation == None)
|
||||
{
|
||||
// We're only here because we have contest stat values. We aren't permitted to have any, so flag it.
|
||||
|
|
@ -41,7 +41,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
else if (correlation == CorrelateSheen)
|
||||
{
|
||||
bool gen3 = data.Info.Generation == 3;
|
||||
bool bdsp = pkm.HasVisitedBDSP(data.Info.EncounterOriginal.Species);
|
||||
bool bdsp = pkm.HasVisitedBDSP(data.Info.EvoChainsAllGens.Gen8b);
|
||||
var method = gen3 ? ContestStatGrantingSheen.Gen3 :
|
||||
bdsp ? ContestStatGrantingSheen.Gen8b : ContestStatGrantingSheen.Gen4;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ private CheckResult VerifyForm(LegalityAnalysis data)
|
|||
break;
|
||||
case Unown when Info.Generation == 2 && form >= 26:
|
||||
return GetInvalid(string.Format(LFormInvalidRange, "Z", form == 26 ? "!" : "?"));
|
||||
case Dialga or Palkia or Giratina or Arceus when form > 0 && pkm.LA: // can change forms with key items
|
||||
case Dialga or Palkia or Giratina or Arceus when form > 0 && pkm is PA8: // can change forms with key items
|
||||
break;
|
||||
case Giratina when form == 1 ^ pkm.HeldItem == 112: // Giratina, Origin form only with Griseous Orb
|
||||
return GetInvalid(LFormItemInvalid);
|
||||
|
|
@ -308,12 +308,14 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
|
|||
Qwilfish when pkm.Form is 1 => arg switch
|
||||
{
|
||||
not 0 when pkm.IsEgg => GetInvalid(LFormArgumentNotAllowed),
|
||||
not 0 when pkm.CurrentLevel < 25 => GetInvalid(LFormArgumentHigh),
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
},
|
||||
Stantler when pkm is PA8 => arg switch
|
||||
Stantler when pkm is PA8 || pkm.HasVisitedLA(data.Info.EvoChainsAllGens.Gen8a) => arg switch
|
||||
{
|
||||
not 0 when pkm.IsEgg => GetInvalid(LFormArgumentNotAllowed),
|
||||
not 0 when pkm.CurrentLevel < 31 => GetInvalid(LFormArgumentHigh),
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
|
|
@ -76,18 +75,14 @@ private void VerifyOTFriendship(LegalityAnalysis data, bool neverOT, int origin,
|
|||
{
|
||||
if (origin < 0)
|
||||
return;
|
||||
|
||||
if (origin <= 2)
|
||||
{
|
||||
// Verify the original friendship value since it cannot change from the value it was assigned in the original generation.
|
||||
// Since some evolutions have different base friendship values, check all possible evolutions for a match.
|
||||
// If none match, then it is not a valid OT friendship.
|
||||
const int vc = 7; // VC transfers use SM personal info
|
||||
var evos = data.Info.EvoChainsAllGens[vc];
|
||||
var fs = pkm.OT_Friendship;
|
||||
if (evos.All(z => GetBaseFriendship(vc, z.Species, z.Form) != fs))
|
||||
data.AddLine(GetInvalid(LegalityCheckStrings.LMemoryStatFriendshipOTBaseEvent));
|
||||
VerifyOTFriendshipVC12(data, pkm);
|
||||
return;
|
||||
}
|
||||
else if (neverOT)
|
||||
|
||||
if (neverOT)
|
||||
{
|
||||
// Verify the original friendship value since it cannot change from the value it was assigned in the original generation.
|
||||
// If none match, then it is not a valid OT friendship.
|
||||
|
|
@ -98,6 +93,30 @@ private void VerifyOTFriendship(LegalityAnalysis data, bool neverOT, int origin,
|
|||
}
|
||||
}
|
||||
|
||||
private void VerifyOTFriendshipVC12(LegalityAnalysis data, PKM pkm)
|
||||
{
|
||||
// Verify the original friendship value since it cannot change from the value it was assigned in the original generation.
|
||||
// Since some evolutions have different base friendship values, check all possible evolutions for a match.
|
||||
// If none match, then it is not a valid OT friendship.
|
||||
// VC transfers use SM personal info
|
||||
var any = IsMatchFriendship(data.Info.EvoChainsAllGens.Gen7, PersonalTable.USUM, pkm.OT_Friendship);
|
||||
if (!any)
|
||||
data.AddLine(GetInvalid(LegalityCheckStrings.LMemoryStatFriendshipOTBaseEvent));
|
||||
}
|
||||
|
||||
private static bool IsMatchFriendship(EvoCriteria[] evos, PersonalTable pt, int fs)
|
||||
{
|
||||
foreach (var z in evos)
|
||||
{
|
||||
if (!pt.IsPresentInGame(z.Species, z.Form))
|
||||
continue;
|
||||
var entry = pt.GetFormEntry(z.Species, z.Form);
|
||||
if (entry.BaseFriendship == fs)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void VerifyOTAffection(LegalityAnalysis data, bool neverOT, int origin, PKM pkm)
|
||||
{
|
||||
if (pkm is not IAffection a)
|
||||
|
|
@ -209,13 +228,10 @@ public static bool GetCanOTHandle(IEncounterTemplate enc, PKM pkm, int generatio
|
|||
|
||||
private static int GetBaseFriendship(int generation, int species, int form) => generation switch
|
||||
{
|
||||
1 => PersonalTable.USUM[species].BaseFriendship,
|
||||
2 => PersonalTable.USUM[species].BaseFriendship,
|
||||
|
||||
6 => PersonalTable.AO[species].BaseFriendship,
|
||||
7 => PersonalTable.USUM[species].BaseFriendship,
|
||||
8 => PersonalTable.SWSH.GetFormEntry(species, form).BaseFriendship,
|
||||
_ => throw new IndexOutOfRangeException(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(generation)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,12 +14,9 @@ public sealed class LegendsArceusVerifier : Verifier
|
|||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.pkm;
|
||||
if (!pk.LA || pk is not PA8 pa)
|
||||
if (pk is not PA8 pa)
|
||||
return;
|
||||
|
||||
CheckLearnset(data, pa);
|
||||
CheckMastery(data, pa);
|
||||
|
||||
if (pa.IsNoble)
|
||||
data.AddLine(GetInvalid(LStatNobleInvalid));
|
||||
if (pa.IsAlpha != data.EncounterMatch is IAlpha { IsAlpha: true })
|
||||
|
|
@ -27,6 +24,9 @@ public override void Verify(LegalityAnalysis data)
|
|||
|
||||
CheckScalars(data, pa);
|
||||
CheckGanbaru(data, pa);
|
||||
|
||||
CheckLearnset(data, pa);
|
||||
CheckMastery(data, pa);
|
||||
}
|
||||
|
||||
private static void CheckGanbaru(LegalityAnalysis data, PA8 pa)
|
||||
|
|
@ -67,7 +67,7 @@ private static void CheckLearnset(LegalityAnalysis data, PA8 pa)
|
|||
|
||||
// Get the bare minimum moveset.
|
||||
Span<int> expect = stackalloc int[4];
|
||||
var minMoveCount = LoadBareMinimumMoveset(data.EncounterMatch, data.Info.EvoChainsAllGens[8], pa, expect);
|
||||
var minMoveCount = LoadBareMinimumMoveset(data.EncounterMatch, data.Info.EvoChainsAllGens, pa, expect);
|
||||
|
||||
// Flag move slots that are empty.
|
||||
for (int i = moveCount; i < minMoveCount; i++)
|
||||
|
|
@ -81,7 +81,7 @@ private static void CheckLearnset(LegalityAnalysis data, PA8 pa)
|
|||
/// <summary>
|
||||
/// Gets the expected minimum count of moves, and modifies the input <see cref="moves"/> with the bare minimum move IDs.
|
||||
/// </summary>
|
||||
private static int LoadBareMinimumMoveset(ISpeciesForm enc, EvoCriteria[] evos, PA8 pa, Span<int> moves)
|
||||
private static int LoadBareMinimumMoveset(ISpeciesForm enc, EvolutionHistory h, PA8 pa, Span<int> moves)
|
||||
{
|
||||
// Get any encounter moves
|
||||
var pt = PersonalTable.LA;
|
||||
|
|
@ -100,6 +100,10 @@ private static int LoadBareMinimumMoveset(ISpeciesForm enc, EvoCriteria[] evos,
|
|||
Span<int> purchased = stackalloc int[purchasedCount];
|
||||
LoadPurchasedMoves(pa, purchased);
|
||||
|
||||
// If it can be leveled up in other games, level it up in other games.
|
||||
if (pa.HasVisitedSWSH(h.Gen8) || pa.HasVisitedBDSP(h.Gen8b))
|
||||
return count;
|
||||
|
||||
// Level up to current level
|
||||
var level = pa.CurrentLevel;
|
||||
moveset.SetLevelUpMoves(pa.Met_Level, level, moves, purchased, count);
|
||||
|
|
@ -108,6 +112,7 @@ private static int LoadBareMinimumMoveset(ISpeciesForm enc, EvoCriteria[] evos,
|
|||
return 4;
|
||||
|
||||
// Evolve and try
|
||||
var evos = h.Gen8a;
|
||||
for (int i = 0; i < evos.Length - 1; i++)
|
||||
{
|
||||
var evo = evos[i];
|
||||
|
|
@ -223,7 +228,7 @@ private static bool CanLearnMoveByLevelUp(LegalityAnalysis data, PA8 pa, int i,
|
|||
// Changing forms do not have separate tutor permissions, so we don't need to bother with form changes.
|
||||
// Level up movepools can grant moves for mastery at lower levels for earlier evolutions... find the minimum.
|
||||
int level = 101;
|
||||
foreach (var evo in data.Info.EvoChainsAllGens[8])
|
||||
foreach (var evo in data.Info.EvoChainsAllGens.Gen8a)
|
||||
{
|
||||
var pt = PersonalTable.LA;
|
||||
var index = pt.GetFormIndex(evo.Species, evo.Form);
|
||||
|
|
|
|||
|
|
@ -44,14 +44,14 @@ private void VerifyMarksPresent(LegalityAnalysis data, IRibbonIndex m)
|
|||
|
||||
if (hasOne)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, mark)));
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, GetRibbonNameSafe(mark))));
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = IsMarkValid(mark, data.pkm, data.EncounterMatch);
|
||||
if (!result)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, mark)));
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, GetRibbonNameSafe(mark))));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +59,14 @@ private void VerifyMarksPresent(LegalityAnalysis data, IRibbonIndex m)
|
|||
}
|
||||
}
|
||||
|
||||
private static string GetRibbonNameSafe(RibbonIndex index)
|
||||
{
|
||||
if (index >= RibbonIndex.MAX_COUNT)
|
||||
return index.ToString();
|
||||
var expect = $"Ribbon{index}";
|
||||
return RibbonStrings.GetName(expect);
|
||||
}
|
||||
|
||||
public static bool IsMarkValid(RibbonIndex mark, PKM pk, IEncounterTemplate enc)
|
||||
{
|
||||
return IsMarkAllowedAny(enc) && IsMarkAllowedSpecific(mark, pk, enc);
|
||||
|
|
@ -128,28 +136,31 @@ public static bool IsMarkAllowedFishing(IEncounterTemplate enc)
|
|||
|
||||
private void VerifyAffixedRibbonMark(LegalityAnalysis data, IRibbonIndex m)
|
||||
{
|
||||
if (m is not PK8 pk8)
|
||||
if (m is not IRibbonSetAffixed a)
|
||||
return;
|
||||
|
||||
var affix = pk8.AffixedRibbon;
|
||||
var affix = a.AffixedRibbon;
|
||||
if (affix == -1) // None
|
||||
return;
|
||||
|
||||
if ((byte)affix > (int)RibbonIndex.MarkSlump)
|
||||
if ((byte)affix > (int)RibbonIndex.MarkSlump) // SW/SH cannot affix anything higher.
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, affix)));
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe((RibbonIndex)affix))));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pk8.Species == (int)Species.Shedinja && data.EncounterOriginal.Species is not (int)Species.Shedinja)
|
||||
if (m is not PKM pk)
|
||||
return;
|
||||
|
||||
if (pk.Species == (int)Species.Shedinja && data.EncounterOriginal.Species is not (int)Species.Shedinja)
|
||||
{
|
||||
VerifyShedinjaAffixed(data, affix, pk8);
|
||||
VerifyShedinjaAffixed(data, affix, pk, m);
|
||||
return;
|
||||
}
|
||||
EnsureHasRibbon(data, pk8, affix);
|
||||
EnsureHasRibbon(data, m, affix);
|
||||
}
|
||||
|
||||
private void VerifyShedinjaAffixed(LegalityAnalysis data, sbyte affix, PK8 pk8)
|
||||
private void VerifyShedinjaAffixed(LegalityAnalysis data, sbyte affix, PKM pk, IRibbonIndex r)
|
||||
{
|
||||
// Does not copy ribbons or marks, but retains the Affixed Ribbon value.
|
||||
// Try re-verifying to see if it could have had the Ribbon/Mark.
|
||||
|
|
@ -157,48 +168,47 @@ private void VerifyShedinjaAffixed(LegalityAnalysis data, sbyte affix, PK8 pk8)
|
|||
var enc = data.EncounterOriginal;
|
||||
if ((byte) affix >= (int) RibbonIndex.MarkLunchtime)
|
||||
{
|
||||
if (!IsMarkValid((RibbonIndex)affix, pk8, enc))
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, (RibbonIndex) affix)));
|
||||
if (!IsMarkValid((RibbonIndex)affix, pk, enc))
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe((RibbonIndex)affix))));
|
||||
return;
|
||||
}
|
||||
|
||||
if (enc.Generation <= 4 && (pk8.Ball != (int)Ball.Poke || IsMoveSetEvolvedShedinja(pk8)))
|
||||
if (enc.Generation <= 4 && (pk.Ball != (int)Ball.Poke || IsMoveSetEvolvedShedinja(pk)))
|
||||
{
|
||||
// Evolved in a prior generation.
|
||||
EnsureHasRibbon(data, pk8, affix);
|
||||
EnsureHasRibbon(data, r, affix);
|
||||
return;
|
||||
}
|
||||
|
||||
var clone = pk8.Clone();
|
||||
var clone = pk.Clone();
|
||||
clone.Species = (int) Species.Nincada;
|
||||
((IRibbonIndex) clone).SetRibbon(affix);
|
||||
var parse = RibbonVerifier.GetRibbonResults(clone, data.Info.EvoChainsAllGens, enc);
|
||||
var expect = $"Ribbon{(RibbonIndex) affix}";
|
||||
var name = RibbonStrings.GetName(expect);
|
||||
var name = GetRibbonNameSafe((RibbonIndex)affix);
|
||||
bool invalid = parse.FirstOrDefault(z => z.Name == name)?.Invalid == true;
|
||||
var severity = invalid ? Severity.Invalid : Severity.Fishy;
|
||||
data.AddLine(Get(string.Format(LRibbonMarkingAffixedF_0, affix), severity));
|
||||
data.AddLine(Get(string.Format(LRibbonMarkingAffixedF_0, name), severity));
|
||||
}
|
||||
|
||||
private static bool IsMoveSetEvolvedShedinja(PK8 pk8)
|
||||
private static bool IsMoveSetEvolvedShedinja(PKM pk)
|
||||
{
|
||||
// Check for gen3/4 exclusive moves that are Ninjask glitch only.
|
||||
if (pk8.HasMove((int) Move.Screech))
|
||||
if (pk.HasMove((int) Move.Screech))
|
||||
return true;
|
||||
if (pk8.HasMove((int) Move.SwordsDance))
|
||||
if (pk.HasMove((int) Move.SwordsDance))
|
||||
return true;
|
||||
if (pk8.HasMove((int) Move.Slash))
|
||||
if (pk.HasMove((int) Move.Slash))
|
||||
return true;
|
||||
if (pk8.HasMove((int) Move.BatonPass))
|
||||
if (pk.HasMove((int) Move.BatonPass))
|
||||
return true;
|
||||
return pk8.HasMove((int)Move.Agility) && !pk8.GetMoveRecordFlag(12); // TR12 (Agility)
|
||||
return pk.HasMove((int)Move.Agility) && pk is PK8 pk8 && !pk8.GetMoveRecordFlag(12); // TR12 (Agility)
|
||||
}
|
||||
|
||||
private void EnsureHasRibbon(LegalityAnalysis data, IRibbonIndex pk8, sbyte affix)
|
||||
private void EnsureHasRibbon(LegalityAnalysis data, IRibbonIndex m, sbyte affix)
|
||||
{
|
||||
var hasRibbon = pk8.GetRibbonIndex((RibbonIndex) affix);
|
||||
var hasRibbon = m.GetRibbonIndex((RibbonIndex) affix);
|
||||
if (!hasRibbon)
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, (RibbonIndex) affix)));
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe((RibbonIndex) affix))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public sealed class MemoryVerifier : Verifier
|
|||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var pkm = data.pkm;
|
||||
if (pkm.BDSP || pkm.LA)
|
||||
if (ShouldHaveNoMemory(data, pkm))
|
||||
{
|
||||
VerifyOTMemoryIs(data, 0, 0, 0, 0);
|
||||
VerifyHTMemoryNone(data, (ITrainerMemories)pkm);
|
||||
|
|
@ -25,6 +25,13 @@ public override void Verify(LegalityAnalysis data)
|
|||
VerifyHTMemory(data);
|
||||
}
|
||||
|
||||
private static bool ShouldHaveNoMemory(LegalityAnalysis data, PKM pkm)
|
||||
{
|
||||
if (pkm.BDSP || pkm.LA)
|
||||
return !pkm.HasVisitedSWSH(data.Info.EvoChainsAllGens.Gen8);
|
||||
return false;
|
||||
}
|
||||
|
||||
private CheckResult VerifyCommonMemory(PKM pkm, int handler, int gen, LegalInfo info, MemoryContext context)
|
||||
{
|
||||
var memory = MemoryVariableSet.Read((ITrainerMemories)pkm, handler);
|
||||
|
|
@ -56,7 +63,7 @@ private CheckResult VerifyCommonMemory(PKM pkm, int handler, int gen, LegalInfo
|
|||
return GetInvalid(string.Format(LMemoryArgBadLocation, memory.Handler));
|
||||
|
||||
// {0} saw {2} carrying {1} on its back. {4} that {3}.
|
||||
case 21 when gen != 6 || !GetCanLearnMachineMove(new PK6 {Species = memory.Variable, EXP = Experience.GetEXP(100, PersonalTable.XY.GetFormIndex(memory.Variable, 0))}, (int)Move.Fly, 6):
|
||||
case 21 when gen != 6 || !PersonalTable.AO.GetFormEntry(memory.Variable, 0).TMHM[101]: // Fly
|
||||
return GetInvalid(string.Format(LMemoryArgBadMove, memory.Handler));
|
||||
|
||||
// {0} used {2} at {1}’s instruction, but it had no effect. {4} that {3}.
|
||||
|
|
@ -71,7 +78,7 @@ private CheckResult VerifyCommonMemory(PKM pkm, int handler, int gen, LegalInfo
|
|||
// {0} battled at {1}’s side against {2} that Dynamaxed. {4} that {3}.
|
||||
case 71 when !GetCanDynamaxTrainer(memory.Variable, 8, handler == 0 ? (GameVersion)pkm.Version : GameVersion.Any):
|
||||
// {0} battled {2} and Dynamaxed upon {1}’s instruction. {4} that {3}.
|
||||
case 72 when !((PersonalInfoSWSH)PersonalTable.SWSH[memory.Variable]).IsPresentInGame:
|
||||
case 72 when !PersonalTable.SWSH.IsSpeciesInGame(memory.Variable):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
|
||||
// Move
|
||||
|
|
@ -88,13 +95,13 @@ private CheckResult VerifyCommonMemory(PKM pkm, int handler, int gen, LegalInfo
|
|||
// {0} saw {1} paying attention to {2}. {4} that {3}.
|
||||
// {0} fought hard until it had to use Struggle when it battled at {1}’s side against {2}. {4} that {3}.
|
||||
// {0} was taken to a Pokémon Nursery by {1} and left with {2}. {4} that {3}.
|
||||
case 9 or 60 or 75 when gen == 8 && !((PersonalInfoSWSH)PersonalTable.SWSH[memory.Variable]).IsPresentInGame:
|
||||
case 9 or 60 or 75 when gen == 8 && !PersonalTable.SWSH.IsSpeciesInGame(memory.Variable):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
|
||||
// {0} had a great chat about {1} with the {2} that it was in a Box with. {4} that {3}.
|
||||
// {0} became good friends with the {2} in a Box, practiced moves with it, and talked about the day that {0} would be praised by {1}. {4} that {3}.
|
||||
// {0} got in a fight with the {2} that it was in a Box with about {1}. {4} that {3}.
|
||||
case 82 or 83 or 87 when !((PersonalInfoSWSH)PersonalTable.SWSH[memory.Variable]).IsPresentInGame:
|
||||
case 82 or 83 or 87 when !PersonalTable.SWSH.IsSpeciesInGame(memory.Variable):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
|
||||
// {0} had a very hard training session with {1}. {4} that {3}.
|
||||
|
|
@ -201,7 +208,7 @@ private void VerifyOTMemory(LegalityAnalysis data)
|
|||
return;
|
||||
}
|
||||
}
|
||||
else if (!CanHaveMemoryForOT(pkm, memoryGen, memory))
|
||||
else if (!CanHaveMemoryForOT(pkm, memoryGen, memory, Info.EvoChainsAllGens))
|
||||
{
|
||||
VerifyOTMemoryIs(data, 0, 0, 0, 0); // empty
|
||||
return;
|
||||
|
|
@ -248,7 +255,7 @@ private void VerifyOTMemory(LegalityAnalysis data)
|
|||
data.AddLine(VerifyCommonMemory(pkm, 0, Info.Generation, Info, context));
|
||||
}
|
||||
|
||||
private static bool CanHaveMemoryForOT(PKM pkm, int origin, int memory)
|
||||
private static bool CanHaveMemoryForOT(PKM pkm, int origin, int memory, EvolutionHistory evos)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
|
|
@ -259,8 +266,8 @@ private static bool CanHaveMemoryForOT(PKM pkm, int origin, int memory)
|
|||
case 7 when pkm.GG: // LGPE does not set memories.
|
||||
case 8 when pkm.GO_HOME: // HOME does not set memories.
|
||||
case 8 when pkm.Met_Location == Locations.HOME8: // HOME does not set memories.
|
||||
case 8 when pkm.BDSP: // BDSP does not set memories.
|
||||
case 8 when pkm.LA: // LA does not set memories.
|
||||
case 8 when pkm.BDSP && !pkm.HasVisitedSWSH(evos.Gen8): // BDSP does not set memories.
|
||||
case 8 when pkm.LA && !pkm.HasVisitedSWSH(evos.Gen8): // LA does not set memories.
|
||||
return false;
|
||||
|
||||
// Eggs cannot have memories
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ public static class ContestStatInfo
|
|||
/// </remarks>
|
||||
private const int BestSheenStat8b = 120;
|
||||
|
||||
public static void SetSuggestedContestStats(this PKM pk, IEncounterTemplate enc)
|
||||
public static void SetSuggestedContestStats(this PKM pk, IEncounterTemplate enc, EvolutionHistory h)
|
||||
{
|
||||
if (pk is not IContestStatsMutable s)
|
||||
return;
|
||||
|
||||
var restrict = GetContestStatRestriction(pk, pk.Generation);
|
||||
var restrict = GetContestStatRestriction(pk, pk.Generation, h);
|
||||
var baseStat = GetReferenceTemplate(enc);
|
||||
if (restrict == None || pk.Species is not (int)Species.Milotic)
|
||||
baseStat.CopyContestStatsTo(s); // reset
|
||||
|
|
@ -42,25 +42,25 @@ public static void SetSuggestedContestStats(this PKM pk, IEncounterTemplate enc)
|
|||
s.SetAllContestStatsTo(MaxContestStat, restrict == NoSheen ? baseStat.CNT_Sheen : MaxContestStat);
|
||||
}
|
||||
|
||||
public static void SetMaxContestStats(this PKM pk, IEncounterTemplate enc)
|
||||
public static void SetMaxContestStats(this PKM pk, IEncounterTemplate enc, EvolutionHistory h)
|
||||
{
|
||||
if (pk is not IContestStatsMutable s)
|
||||
return;
|
||||
var restrict = GetContestStatRestriction(pk, enc.Generation);
|
||||
var restrict = GetContestStatRestriction(pk, enc.Generation, h);
|
||||
var baseStat = GetReferenceTemplate(enc);
|
||||
if (restrict == None)
|
||||
return;
|
||||
s.SetAllContestStatsTo(MaxContestStat, restrict == NoSheen ? baseStat.CNT_Sheen : MaxContestStat);
|
||||
}
|
||||
|
||||
public static ContestStatGranting GetContestStatRestriction(PKM pk, int origin) => origin switch
|
||||
public static ContestStatGranting GetContestStatRestriction(PKM pk, int origin, EvolutionHistory h) => origin switch
|
||||
{
|
||||
3 => pk.Format < 6 ? CorrelateSheen : Mixed,
|
||||
4 => pk.Format < 6 ? CorrelateSheen : Mixed,
|
||||
|
||||
5 => pk.Format >= 6 ? NoSheen : None, // ORAS Contests
|
||||
6 => pk.AO || !pk.IsUntraded ? NoSheen : None,
|
||||
8 => pk.BDSP ? CorrelateSheen : None, // BDSP Contests
|
||||
8 => pk.HasVisitedBDSP(h.Gen8b) ? CorrelateSheen : None, // BDSP Contests
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,14 @@ public override void Verify(LegalityAnalysis data)
|
|||
if (enc is IEncounterServerDate { IsDateRestricted: true } serverGift)
|
||||
{
|
||||
var date = new DateTime(pkm.Met_Year + 2000, pkm.Met_Month, pkm.Met_Day);
|
||||
|
||||
// HOME Gifts for Sinnoh/Hisui starters were forced JPN until May 20, 2022 (UTC).
|
||||
if (enc is WB8 { CardID: 9015 or 9016 or 9017 } or WA8 { CardID: 9018 or 9019 or 9020 })
|
||||
{
|
||||
if (date < new DateTime(2022, 5, 20) && pkm.Language != (int)LanguageID.Japanese)
|
||||
data.AddLine(GetInvalid(LDateOutsideDistributionWindow));
|
||||
}
|
||||
|
||||
var result = serverGift.IsValidDate(date);
|
||||
if (result == EncounterServerDateCheck.Invalid)
|
||||
data.AddLine(GetInvalid(LDateOutsideDistributionWindow));
|
||||
|
|
@ -369,8 +377,8 @@ private static void VerifyReceivability(LegalityAnalysis data, MysteryGift g)
|
|||
case WC6 wc6 when !wc6.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
|
||||
case WC7 wc7 when !wc7.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
|
||||
case WC8 wc8 when !wc8.CanBeReceivedByVersion(pkm.Version):
|
||||
case WB8 wb8 when !wb8.CanBeReceivedByVersion(pkm.Version):
|
||||
case WA8 wa8 when !wa8.CanBeReceivedByVersion(pkm.Version):
|
||||
case WB8 wb8 when !wb8.CanBeReceivedByVersion(pkm.Version, pkm):
|
||||
case WA8 wa8 when !wa8.CanBeReceivedByVersion(pkm.Version, pkm):
|
||||
data.AddLine(GetInvalid(LEncGiftVersionNotDistributed, GameOrigin));
|
||||
return;
|
||||
case WC6 wc6 when wc6.RestrictLanguage != 0 && pkm.Language != wc6.RestrictLanguage:
|
||||
|
|
@ -423,9 +431,9 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pkm)
|
|||
if (pkm.IsEgg)
|
||||
{
|
||||
if (pkm.Fullness != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, 0), Encounter));
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "0"), Encounter));
|
||||
if (pkm.Enjoyment != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, 0), Encounter));
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, "0"), Encounter));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -433,8 +441,11 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pkm)
|
|||
{
|
||||
if (pkm.Fullness > 245) // Exiting camp is -10
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "<=245"), Encounter));
|
||||
else if (pkm.Fullness is not 0 && pkm is not PK8)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "0"), Encounter));
|
||||
|
||||
if (pkm.Enjoyment != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, 0), Encounter));
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, "0"), Encounter));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +459,7 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pkm)
|
|||
return; // evolved
|
||||
|
||||
if (Unfeedable.Contains(pkm.Species))
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, 0), Encounter));
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "0"), Encounter));
|
||||
}
|
||||
|
||||
private static readonly HashSet<int> Unfeedable = new()
|
||||
|
|
@ -488,7 +499,7 @@ private static void VerifyAbsoluteSizes(LegalityAnalysis data, IScaledSizeValue
|
|||
|
||||
private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
|
||||
{
|
||||
if (pk8.Favorite)
|
||||
if (pk8.Favorite && !pk8.GG)
|
||||
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
|
||||
|
||||
var social = pk8.Sociability;
|
||||
|
|
@ -504,12 +515,8 @@ private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
|
|||
|
||||
VerifyStatNature(data, pk8);
|
||||
|
||||
var bv = pk8.BattleVersion;
|
||||
if (bv != 0)
|
||||
{
|
||||
if ((bv != (int)GameVersion.SW && bv != (int)GameVersion.SH) || pk8.SWSH)
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
}
|
||||
if (!pk8.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
|
||||
var enc = data.EncounterMatch;
|
||||
bool originGMax = enc is IGigantamax {CanGigantamax: true};
|
||||
|
|
@ -556,12 +563,15 @@ private void VerifyPLAStats(LegalityAnalysis data, PA8 pa8)
|
|||
{
|
||||
VerifyAbsoluteSizes(data, pa8);
|
||||
|
||||
if (pa8.Favorite)
|
||||
if (pa8.Favorite && !pa8.GG)
|
||||
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
|
||||
|
||||
var affix = pa8.AffixedRibbon;
|
||||
if (affix != -1) // None
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, affix)));
|
||||
if (!pa8.HasVisitedSWSH(data.Info.EvoChainsAllGens.Gen8))
|
||||
{
|
||||
var affix = pa8.AffixedRibbon;
|
||||
if (affix != -1) // None
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, affix)));
|
||||
}
|
||||
|
||||
var social = pa8.Sociability;
|
||||
if (social != 0)
|
||||
|
|
@ -569,8 +579,7 @@ private void VerifyPLAStats(LegalityAnalysis data, PA8 pa8)
|
|||
|
||||
VerifyStatNature(data, pa8);
|
||||
|
||||
var bv = pa8.BattleVersion;
|
||||
if (bv != 0)
|
||||
if (!pa8.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
|
||||
if (pa8.CanGigantamax)
|
||||
|
|
@ -588,12 +597,15 @@ private void VerifyPLAStats(LegalityAnalysis data, PA8 pa8)
|
|||
|
||||
private void VerifyBDSPStats(LegalityAnalysis data, PB8 pb8)
|
||||
{
|
||||
if (pb8.Favorite)
|
||||
if (pb8.Favorite && !pb8.GG)
|
||||
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
|
||||
|
||||
var affix = pb8.AffixedRibbon;
|
||||
if (affix != -1) // None
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, affix)));
|
||||
if (!pb8.HasVisitedSWSH(data.Info.EvoChainsAllGens.Gen8))
|
||||
{
|
||||
var affix = pb8.AffixedRibbon;
|
||||
if (affix != -1) // None
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, affix)));
|
||||
}
|
||||
|
||||
var social = pb8.Sociability;
|
||||
if (social != 0)
|
||||
|
|
@ -601,11 +613,14 @@ private void VerifyBDSPStats(LegalityAnalysis data, PB8 pb8)
|
|||
|
||||
if (pb8.IsDprIllegal)
|
||||
data.AddLine(GetInvalid(LTransferFlagIllegal));
|
||||
if (pb8.Species is (int)Species.Spinda or (int)Species.Nincada && !pb8.BDSP)
|
||||
data.AddLine(GetInvalid(LTransferNotPossible));
|
||||
if (pb8.Species is (int)Species.Spinda && pb8.Tracker != 0)
|
||||
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
||||
|
||||
VerifyStatNature(data, pb8);
|
||||
|
||||
var bv = pb8.BattleVersion;
|
||||
if (bv != 0)
|
||||
if (!pb8.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
|
||||
if (pb8.CanGigantamax)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ private void VerifyFixedNicknameEncounter(LegalityAnalysis data, ILangNicknamedT
|
|||
if (n is WC3 && pkm.Format != 3)
|
||||
{
|
||||
// Gen3 gifts transferred to Generation 4 from another language can set the nickname flag.
|
||||
var evos = data.Info.EvoChainsAllGens[3];
|
||||
var evos = data.Info.EvoChainsAllGens.Gen3;
|
||||
bool matchAny = evos.Any(evo => !SpeciesName.IsNicknamedAnyLanguage(evo.Species, nickname, 3));
|
||||
if (matchAny)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -133,6 +133,13 @@ private static void VerifyEC(LegalityAnalysis data)
|
|||
// Gen1-2, Gen6+ should have PID != EC
|
||||
if (pkm.PID == pkm.EncryptionConstant)
|
||||
{
|
||||
// Check for edge cases
|
||||
var enc = Info.EncounterMatch;
|
||||
if (enc is WA8 {IsEquivalentFixedECPID: true})
|
||||
return;
|
||||
if (enc is WB8 {IsEquivalentFixedECPID: true})
|
||||
return;
|
||||
|
||||
data.AddLine(GetInvalid(LPIDEqualsEC, CheckIdentifier.EC)); // better to flag than 1:2^32 odds since RNG is not feasible to yield match
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
}
|
||||
}
|
||||
|
||||
private static List<string> GetIncorrectRibbons(PKM pkm, EvoCriteria[][] evos, IEncounterTemplate enc)
|
||||
private static List<string> GetIncorrectRibbons(PKM pkm, EvolutionHistory evos, IEncounterTemplate enc)
|
||||
{
|
||||
List<string> missingRibbons = new();
|
||||
List<string> invalidRibbons = new();
|
||||
|
|
@ -75,14 +75,14 @@ private static bool GetIncorrectRibbonsEgg(PKM pkm, IEncounterTemplate enc)
|
|||
return false;
|
||||
}
|
||||
|
||||
internal static IEnumerable<RibbonResult> GetRibbonResults(PKM pkm, EvoCriteria[][] evos, IEncounterTemplate enc)
|
||||
internal static IEnumerable<RibbonResult> GetRibbonResults(PKM pkm, EvolutionHistory evos, IEncounterTemplate enc)
|
||||
{
|
||||
return GetInvalidRibbons(pkm, evos, enc)
|
||||
.Concat(GetInvalidRibbonsEvent1(pkm, enc))
|
||||
.Concat(GetInvalidRibbonsEvent2(pkm, enc));
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, EvoCriteria[][] evos, IEncounterTemplate enc)
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, EvolutionHistory evos, IEncounterTemplate enc)
|
||||
{
|
||||
// is a part of Event4, but O3 doesn't have the others
|
||||
if (pkm is IRibbonSetOnly3 {RibbonWorld: true})
|
||||
|
|
@ -133,7 +133,7 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, EvoCriteria[
|
|||
var iterate = GetInvalidRibbons4Any(pkm, evos, s4, gen);
|
||||
if (!inhabited4)
|
||||
{
|
||||
if (pkm.BDSP) // Allow Sinnoh Champion. ILCA reused the Gen4 ribbon for the remake.
|
||||
if (pkm.HasVisitedBDSP(evos.Gen8b)) // Allow Sinnoh Champion. ILCA reused the Gen4 ribbon for the remake.
|
||||
iterate = iterate.Concat(GetInvalidRibbonsNoneSkipIndex(s4.RibbonBitsOnly(), s4.RibbonNamesOnly(), 1));
|
||||
else
|
||||
iterate = iterate.Concat(GetInvalidRibbonsNone(s4.RibbonBitsOnly(), s4.RibbonNamesOnly()));
|
||||
|
|
@ -148,7 +148,7 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, EvoCriteria[
|
|||
var iterate = inhabited6
|
||||
? GetInvalidRibbons6Any(pkm, s6, gen, enc)
|
||||
: pkm.Format >= 8
|
||||
? GetInvalidRibbons6AnyG8(pkm, s6)
|
||||
? GetInvalidRibbons6AnyG8(pkm, s6, evos)
|
||||
: GetInvalidRibbonsNone(s6.RibbonBits(), s6.RibbonNamesBool());
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
|
|
@ -187,7 +187,7 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, EvoCriteria[
|
|||
if (pkm is IRibbonSetCommon8 s8)
|
||||
{
|
||||
bool inhabited8 = gen <= 8;
|
||||
var iterate = inhabited8 ? GetInvalidRibbons8Any(pkm, s8, enc) : GetInvalidRibbonsNone(s8.RibbonBits(), s8.RibbonNames());
|
||||
var iterate = inhabited8 ? GetInvalidRibbons8Any(pkm, s8, enc, evos) : GetInvalidRibbonsNone(s8.RibbonBits(), s8.RibbonNames());
|
||||
foreach (var z in iterate)
|
||||
yield return z;
|
||||
}
|
||||
|
|
@ -207,14 +207,14 @@ private static IEnumerable<RibbonResult> GetMissingContestRibbons(IReadOnlyList<
|
|||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons4Any(PKM pkm, EvoCriteria[][] evos, IRibbonSetCommon4 s4, int gen)
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons4Any(PKM pkm, EvolutionHistory evos, IRibbonSetCommon4 s4, int gen)
|
||||
{
|
||||
if (s4.RibbonRecord)
|
||||
yield return new RibbonResult(nameof(s4.RibbonRecord)); // Unobtainable
|
||||
if (s4.RibbonFootprint && !CanHaveFootprintRibbon(pkm, evos, gen))
|
||||
yield return new RibbonResult(nameof(s4.RibbonFootprint));
|
||||
|
||||
bool visitBDSP = pkm.BDSP;
|
||||
bool visitBDSP = pkm.HasVisitedBDSP(evos.Gen8b);
|
||||
bool gen34 = gen is 3 or 4;
|
||||
bool not6 = pkm.Format < 6 || gen is > 6 or < 3;
|
||||
bool noDaily = !gen34 && not6 && !visitBDSP;
|
||||
|
|
@ -287,9 +287,9 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons6Any(PKM pkm, IRibbonS
|
|||
yield return result;
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons6AnyG8(PKM pkm, IRibbonSetCommon6 s6)
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons6AnyG8(PKM pkm, IRibbonSetCommon6 s6, EvolutionHistory evos)
|
||||
{
|
||||
if (!pkm.BDSP)
|
||||
if (!pkm.HasVisitedBDSP(evos.Gen8b))
|
||||
{
|
||||
var none = GetInvalidRibbonsNone(s6.RibbonBits(), s6.RibbonNamesBool());
|
||||
foreach (var x in none)
|
||||
|
|
@ -417,14 +417,21 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons7Any(PKM pkm, IRibbonS
|
|||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons8Any(PKM pkm, IRibbonSetCommon8 s8, IEncounterTemplate enc)
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbons8Any(PKM pkm, IRibbonSetCommon8 s8, IEncounterTemplate enc, EvolutionHistory evos)
|
||||
{
|
||||
if (!pkm.InhabitedGeneration(8) || !((PersonalInfoSWSH)PersonalTable.SWSH[pkm.Species]).IsPresentInGame || pkm.BDSP)
|
||||
bool swsh = pkm.HasVisitedSWSH(evos.Gen8);
|
||||
bool bdsp = pkm.HasVisitedBDSP(evos.Gen8b);
|
||||
bool pla = pkm.HasVisitedLA(evos.Gen8a);
|
||||
|
||||
if (!swsh && !bdsp)
|
||||
{
|
||||
if (s8.RibbonTowerMaster)
|
||||
yield return new RibbonResult(nameof(s8.RibbonTowerMaster));
|
||||
}
|
||||
if (!swsh)
|
||||
{
|
||||
if (s8.RibbonChampionGalar)
|
||||
yield return new RibbonResult(nameof(s8.RibbonChampionGalar));
|
||||
if (s8.RibbonTowerMaster && !(pkm.SWSH || pkm.BDSP) && pkm.IsUntraded)
|
||||
yield return new RibbonResult(nameof(s8.RibbonTowerMaster));
|
||||
if (s8.RibbonMasterRank)
|
||||
yield return new RibbonResult(nameof(s8.RibbonMasterRank));
|
||||
}
|
||||
|
|
@ -440,15 +447,10 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons8Any(PKM pkm, IRibbonS
|
|||
|
||||
// Legends cannot compete in Ranked, thus cannot reach Master Rank and obtain the ribbon.
|
||||
// Past gen Pokemon can get the ribbon only if they've been reset.
|
||||
if (s8.RibbonMasterRank && !CanParticipateInRankedSWSH(pkm, enc))
|
||||
if (s8.RibbonMasterRank && !CanParticipateInRankedSWSH(pkm, enc, evos))
|
||||
yield return new RibbonResult(nameof(s8.RibbonMasterRank));
|
||||
|
||||
if (s8.RibbonTowerMaster)
|
||||
{
|
||||
if (!(pkm.SWSH || pkm.BDSP) && pkm.IsUntraded)
|
||||
yield return new RibbonResult(nameof(s8.RibbonTowerMaster));
|
||||
}
|
||||
else
|
||||
if (!s8.RibbonTowerMaster)
|
||||
{
|
||||
// If the Tower Master ribbon is not present but a memory hint implies it should...
|
||||
// This memory can also be applied in Gen6/7 via defeating the Chatelaines, where legends are disallowed.
|
||||
|
|
@ -461,22 +463,26 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons8Any(PKM pkm, IRibbonS
|
|||
}
|
||||
}
|
||||
|
||||
// can be expanded upon if SWSH gets updated with the new ribbon when HOME has BDSP support
|
||||
if (s8.RibbonTwinklingStar && (pkm is IRibbonSetCommon6 {RibbonContestStar:false} || !pkm.BDSP))
|
||||
if (s8.RibbonTwinklingStar && (!bdsp || pkm is IRibbonSetCommon6 {RibbonContestStar:false}))
|
||||
{
|
||||
yield return new RibbonResult(nameof(s8.RibbonTwinklingStar));
|
||||
}
|
||||
|
||||
// received when capturing photos with Pokémon in the Photography Studio
|
||||
if (s8.RibbonPioneer && !pkm.LA)
|
||||
if (s8.RibbonPioneer && !pla)
|
||||
{
|
||||
yield return new RibbonResult(nameof(s8.RibbonPioneer));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CanParticipateInRankedSWSH(PKM pkm, IEncounterTemplate enc)
|
||||
private static bool CanParticipateInRankedSWSH(PKM pkm, IEncounterTemplate enc, EvolutionHistory evos)
|
||||
{
|
||||
if (!pkm.SWSH && pkm is IBattleVersion {BattleVersion: 0})
|
||||
bool exist = enc.Generation switch
|
||||
{
|
||||
< 8 => pkm is IBattleVersion { BattleVersion: (int)GameVersion.SW or (int)GameVersion.SH },
|
||||
_ => pkm.HasVisitedSWSH(evos.Gen8),
|
||||
};
|
||||
if (!exist)
|
||||
return false;
|
||||
|
||||
// Clamp to permitted species
|
||||
|
|
@ -490,14 +496,14 @@ private static bool CanParticipateInRankedSWSH(PKM pkm, IEncounterTemplate enc)
|
|||
if (Legal.Mythicals.Contains(species))
|
||||
return false;
|
||||
|
||||
if (enc.Version == GameVersion.GO) // Capture date is global time, and not console changeable.
|
||||
if (enc.Version == GameVersion.GO || enc is IEncounterServerDate { IsDateRestricted: true }) // Capture date is global time, and not console changeable.
|
||||
{
|
||||
if (pkm.MetDate > new DateTime(2022, 9, 1)) // Series 12 end date
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var pi = (PersonalInfoSWSH)PersonalTable.SWSH[species];
|
||||
return pi.IsPresentInGame;
|
||||
|
||||
return PersonalTable.SWSH.IsPresentInGame(species, pkm.Form);
|
||||
}
|
||||
|
||||
private static IEnumerable<RibbonResult> GetInvalidRibbonsEvent1(PKM pkm, IEncounterTemplate enc)
|
||||
|
|
@ -582,7 +588,7 @@ private static bool IsAllowedBattleFrontier(int species, int form, int gen)
|
|||
return IsAllowedBattleFrontier(species);
|
||||
}
|
||||
|
||||
private static bool CanHaveFootprintRibbon(PKM pkm, EvoCriteria[][] evos, int gen)
|
||||
private static bool CanHaveFootprintRibbon(PKM pkm, EvolutionHistory evos, int gen)
|
||||
{
|
||||
if (gen <= 4) // Friendship Check unnecessary - can decrease after obtaining ribbon.
|
||||
return true;
|
||||
|
|
@ -595,15 +601,16 @@ private static bool CanHaveFootprintRibbon(PKM pkm, EvoCriteria[][] evos, int ge
|
|||
return true;
|
||||
|
||||
// Gen8-BDSP: Variable by species Footprint
|
||||
if (pkm.BDSP)
|
||||
var bdspEvos = evos.Gen8b;
|
||||
if (pkm.HasVisitedBDSP(bdspEvos))
|
||||
{
|
||||
if (evos[8].Any(z => z.Species <= Legal.MaxSpeciesID_4 && !HasFootprintBDSP[z.Species]))
|
||||
if (bdspEvos.Any(z => PersonalTable.BDSP.IsPresentInGame(z.Species, z.Form) && !HasFootprintBDSP[z.Species]))
|
||||
return true; // no footprint
|
||||
if (pkm.CurrentLevel - pkm.Met_Level >= 30)
|
||||
return true; // traveled well
|
||||
}
|
||||
|
||||
// Gen8: Can't obtain
|
||||
// Otherwise: Can't obtain
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,8 +138,7 @@ public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
|||
|
||||
// PK8
|
||||
int species = pkm.Species;
|
||||
var pi = (PersonalInfoSWSH)PersonalTable.SWSH.GetFormEntry(species, pkm.Form);
|
||||
if (!pi.IsPresentInGame || pkm.BDSP || pkm.LA) // Can't transfer
|
||||
if (!PersonalTable.SWSH.IsPresentInGame(species, pkm.Form)) // Can't transfer
|
||||
{
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
return;
|
||||
|
|
@ -150,7 +149,7 @@ public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
|||
{
|
||||
VerifyHOMETracker(data, pkm);
|
||||
}
|
||||
else if (enc.Generation < 8 && pkm.Format >= 8)
|
||||
else if (pkm.Format >= 8 && !pkm.IsNative)
|
||||
{
|
||||
if (enc is EncounterStatic7 {IsTotem: true} s)
|
||||
{
|
||||
|
|
@ -160,19 +159,18 @@ public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
|||
data.AddLine(GetInvalid(LTransferBad));
|
||||
}
|
||||
|
||||
VerifyHOMETransfer(data, pkm);
|
||||
if (enc.Generation < 8)
|
||||
VerifyHOMETransfer(data, pkm);
|
||||
VerifyHOMETracker(data, pkm);
|
||||
}
|
||||
}
|
||||
private void VerifyTransferLegalityG8a(LegalityAnalysis data, PA8 pk)
|
||||
{
|
||||
// Tracker value is set via Transfer across HOME.
|
||||
// No HOME access yet.
|
||||
if (pk is IHomeTrack { Tracker: not 0 })
|
||||
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
||||
if (!pk.IsNative)
|
||||
VerifyHOMETracker(data, pk);
|
||||
|
||||
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(pk.Species, pk.Form);
|
||||
if (!pi.IsPresentInGame || !pk.LA) // Can't transfer
|
||||
if (!PersonalTable.LA.IsPresentInGame(pk.Species, pk.Form)) // Can't transfer
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
}
|
||||
|
||||
|
|
@ -180,12 +178,10 @@ private void VerifyTransferLegalityG8a(LegalityAnalysis data, PA8 pk)
|
|||
private void VerifyTransferLegalityG8b(LegalityAnalysis data, PB8 pk)
|
||||
{
|
||||
// Tracker value is set via Transfer across HOME.
|
||||
// No HOME access yet.
|
||||
if (pk is IHomeTrack { Tracker: not 0 })
|
||||
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
||||
if (!pk.IsNative)
|
||||
VerifyHOMETracker(data, pk);
|
||||
|
||||
var pi = (PersonalInfoBDSP)PersonalTable.BDSP.GetFormEntry(pk.Species, pk.Form);
|
||||
if (!pi.IsPresentInGame || !pk.BDSP) // Can't transfer
|
||||
if (!PersonalTable.BDSP.IsPresentInGame(pk.Species, pk.Form)) // Can't transfer
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +215,9 @@ public IEnumerable<CheckResult> VerifyVCEncounter(PKM pkm, IEncounterTemplate en
|
|||
{
|
||||
if (pkm.Met_Location != transfer.Location)
|
||||
yield return GetInvalid(LTransferMetLocation);
|
||||
if (pkm.Egg_Location != transfer.EggLocation)
|
||||
|
||||
var expecteEgg = pkm is PB8 ? Locations.Default8bNone : transfer.EggLocation;
|
||||
if (pkm.Egg_Location != expecteEgg)
|
||||
yield return GetInvalid(LEggLocationNone);
|
||||
|
||||
// Flag Moves that cannot be transferred
|
||||
|
|
|
|||
|
|
@ -161,6 +161,12 @@ public virtual Shiny Shiny
|
|||
public virtual bool EggEncounter => IsEgg;
|
||||
public abstract int EggLocation { get; set; }
|
||||
|
||||
protected virtual bool IsMatchEggLocation(PKM pk)
|
||||
{
|
||||
var expect = EggEncounter ? EggLocation : pk is PB8 ? Locations.Default8bNone : 0;
|
||||
return pk.Egg_Location == expect;
|
||||
}
|
||||
|
||||
public Ball FixedBall => (Ball)Ball;
|
||||
|
||||
public int TrainerID7 => (int)((uint)(TID | (SID << 16)) % 1000000);
|
||||
|
|
|
|||
|
|
@ -141,6 +141,8 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
{
|
||||
// met location: deferred to general transfer check
|
||||
if (wc.CurrentLevel > pkm.Met_Level) return false;
|
||||
if (!IsMatchEggLocation(pkm))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -372,7 +372,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
if (OriginGame != 0 && OriginGame != pkm.Version) return false;
|
||||
if (Language != 0 && Language != pkm.Language) return false;
|
||||
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -26,8 +26,9 @@ public enum GiftType : byte
|
|||
public WA8() : this(new byte[Size]) { }
|
||||
public WA8(byte[] data) : base(data) { }
|
||||
|
||||
public bool CanBeReceivedByVersion(int v) => v is (int) GameVersion.PLA;
|
||||
public bool CanBeReceivedByVersion(int v, PKM pk) => v is (int) GameVersion.PLA || (pk is PK8 && v is (int)GameVersion.SW);
|
||||
public bool IsDateRestricted => true;
|
||||
public bool IsEquivalentFixedECPID => EncryptionConstant != 0 && PIDType == ShinyType8.FixedValue && PID == EncryptionConstant;
|
||||
|
||||
// General Card Properties
|
||||
public override int CardID
|
||||
|
|
@ -403,7 +404,7 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
|
|||
Species = Species,
|
||||
Form = Form,
|
||||
CurrentLevel = currentLevel,
|
||||
Ball = Ball != 0 ? Ball : 4, // Default is Pokeball
|
||||
Ball = Ball != 0 ? Ball : (int)Core.Ball.LAPoke, // Default is Pokeball
|
||||
Met_Level = metLevel,
|
||||
HeldItem = HeldItem,
|
||||
|
||||
|
|
@ -441,15 +442,22 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
|
|||
EV_SPA = EV_SPA,
|
||||
EV_SPD = EV_SPD,
|
||||
|
||||
CanGigantamax = CanGigantamax,
|
||||
DynamaxLevel = DynamaxLevel,
|
||||
GV_HP = GV_HP,
|
||||
GV_ATK = GV_ATK,
|
||||
GV_DEF = GV_DEF,
|
||||
GV_SPE = GV_SPE,
|
||||
GV_SPA = GV_SPA,
|
||||
GV_SPD = GV_SPD,
|
||||
|
||||
//CanGigantamax = CanGigantamax,
|
||||
//DynamaxLevel = DynamaxLevel,
|
||||
|
||||
Met_Location = MetLocation,
|
||||
Egg_Location = EggLocation,
|
||||
};
|
||||
pk.SetMaximumPPCurrent();
|
||||
|
||||
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version))
|
||||
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version, pk))
|
||||
pk.Version = (int)GameVersion.PLA;
|
||||
|
||||
if (OTGender >= 2)
|
||||
|
|
@ -578,7 +586,7 @@ private void SetIVs(PKM pk)
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
if (!IsEgg)
|
||||
{
|
||||
if (OTGender < 2)
|
||||
{
|
||||
|
|
@ -623,8 +631,17 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
else
|
||||
{
|
||||
if (!Shiny.IsValid(pkm)) return false;
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (pkm is PK8)
|
||||
{
|
||||
if (pkm.Met_Location != Locations.HOME_SWLA)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MetLocation != pkm.Met_Location)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (MetLevel != 0 && MetLevel != pkm.Met_Level) return false;
|
||||
|
|
@ -636,13 +653,15 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
var expectedBall = (Ball == 0 ? poke : Ball);
|
||||
if (expectedBall < poke) // Not even Cherish balls are safe! They get set to the proto-Poké ball.
|
||||
expectedBall = poke;
|
||||
if (pkm is PK8)
|
||||
expectedBall = (int)Core.Ball.Poke; // Transferred to SWSH -> Regular Poké ball
|
||||
if (expectedBall != pkm.Ball)
|
||||
return false;
|
||||
|
||||
if (pkm is IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
|
||||
if (pkm is IDynamaxLevel dl && dl.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
|
||||
if (pkm is not IDynamaxLevel dl || dl.DynamaxLevel < DynamaxLevel)
|
||||
if (pkm is IGanbaru b && b.IsGanbaruValuesBelow(this))
|
||||
return false;
|
||||
|
||||
// PID Types 0 and 1 do not use the fixed PID value.
|
||||
|
|
|
|||
|
|
@ -531,7 +531,7 @@ public bool CanBeAnyLanguage()
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
if (!IsEgg)
|
||||
{
|
||||
if (OTGender != 3)
|
||||
{
|
||||
|
|
@ -544,6 +544,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
if (OriginGame != 0 && OriginGame != pkm.Version) return false;
|
||||
if (EncryptionConstant != 0 && EncryptionConstant != pkm.EncryptionConstant) return false;
|
||||
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (!CanBeAnyLanguage() && !CanHaveLanguage(pkm.Language))
|
||||
return false;
|
||||
}
|
||||
|
|
@ -569,7 +570,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
else
|
||||
{
|
||||
if (!Shiny.IsValid(pkm)) return false;
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace PKHeX.Core
|
|||
/// <summary>
|
||||
/// Generation 8b Mystery Gift Template File
|
||||
/// </summary>
|
||||
public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, IContestStats, ILangNicknamedTemplate,
|
||||
public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, IContestStats, ILangNicknamedTemplate, IEncounterServerDate,
|
||||
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8
|
||||
{
|
||||
public const int Size = 0x2DC;
|
||||
|
|
@ -16,6 +16,9 @@ public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, ICo
|
|||
|
||||
public override int Generation => 8;
|
||||
|
||||
public bool IsDateRestricted => IsHOMEGift;
|
||||
public bool IsEquivalentFixedECPID => EncryptionConstant != 0 && PIDType == ShinyType8.FixedValue && PID == EncryptionConstant;
|
||||
|
||||
public enum GiftType : byte
|
||||
{
|
||||
None = 0,
|
||||
|
|
@ -32,7 +35,7 @@ public enum GiftType : byte
|
|||
|
||||
// TODO: public byte RestrictVersion?
|
||||
|
||||
public bool CanBeReceivedByVersion(int v) => v is (int) GameVersion.BD or (int) GameVersion.SP;
|
||||
public bool CanBeReceivedByVersion(int v, PKM pk) => v is (int) GameVersion.BD or (int) GameVersion.SP || (pk is PK8 && Locations.IsValidMetBDSP(pk.Met_Location, pk.Version));
|
||||
|
||||
// General Card Properties
|
||||
|
||||
|
|
@ -182,7 +185,7 @@ public override int HeldItem
|
|||
|
||||
public int MetLevel { get => Data[CardStart + 0x291]; set => Data[CardStart + 0x291] = (byte)value; }
|
||||
|
||||
// Ribbons 0x24C-0x26C
|
||||
// Ribbons 0x292-0x2B2
|
||||
private const int RibbonBytesOffset = 0x292;
|
||||
private const int RibbonBytesCount = 0x20;
|
||||
private const int RibbonByteNone = 0xFF; // signed -1
|
||||
|
|
@ -366,6 +369,8 @@ private static int GetOTOffset(int language)
|
|||
return 0x150 + (index * 0x20);
|
||||
}
|
||||
|
||||
public bool IsHOMEGift => CardID >= 9000;
|
||||
|
||||
public bool CanHandleOT(int language) => !GetHasOT(language);
|
||||
|
||||
public override GameVersion Version
|
||||
|
|
@ -447,12 +452,12 @@ public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
|
|||
}
|
||||
pk.SetMaximumPPCurrent();
|
||||
|
||||
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version))
|
||||
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version, pk))
|
||||
{
|
||||
// give random valid game
|
||||
var rnd = Util.Rand;
|
||||
do { pk.Version = (int)GameVersion.BD + rnd.Next(2); }
|
||||
while (!CanBeReceivedByVersion(pk.Version));
|
||||
while (!CanBeReceivedByVersion(pk.Version, pk));
|
||||
}
|
||||
|
||||
if (pk.TID == 0 && pk.SID == 0)
|
||||
|
|
@ -581,7 +586,7 @@ private void SetIVs(PKM pk)
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if ((short)pkm.Egg_Location == Locations.Default8bNone) // Not Egg
|
||||
if (!IsEgg)
|
||||
{
|
||||
if (OTGender < 2)
|
||||
{
|
||||
|
|
@ -627,8 +632,17 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
else
|
||||
{
|
||||
if (!Shiny.IsValid(pkm)) return false;
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (pkm is PK8)
|
||||
{
|
||||
if (!Locations.IsValidMetBDSP(pkm.Met_Location, pkm.Version))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MetLocation != pkm.Met_Location)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (MetLevel != 0 && MetLevel != pkm.Met_Level) return false;
|
||||
|
|
@ -646,6 +660,12 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
return pkm.PID == GetPID(pkm, type);
|
||||
}
|
||||
|
||||
protected override bool IsMatchEggLocation(PKM pk)
|
||||
{
|
||||
var expect = pk is PB8 ? Locations.Default8bNone : 0;
|
||||
return pk.Egg_Location == expect;
|
||||
}
|
||||
|
||||
protected override bool IsMatchDeferred(PKM pkm) => Species != pkm.Species;
|
||||
protected override bool IsMatchPartial(PKM pkm) => false; // no version compatibility checks yet.
|
||||
|
||||
|
|
|
|||
|
|
@ -520,7 +520,7 @@ private void SetIVs(PKM pk)
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
if (!IsEgg)
|
||||
{
|
||||
if (OTGender != 3)
|
||||
{
|
||||
|
|
@ -559,7 +559,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -556,7 +556,7 @@ public bool IsAshGreninjaWC7(PKM pkm)
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
if (!IsEgg)
|
||||
{
|
||||
if (OTGender != 3)
|
||||
{
|
||||
|
|
@ -591,7 +591,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
else
|
||||
{
|
||||
if (!Shiny.IsValid(pkm)) return false;
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -581,7 +581,7 @@ private void SetIVs(PKM pk)
|
|||
|
||||
public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
if (!IsEgg)
|
||||
{
|
||||
if (OTGender < 2)
|
||||
{
|
||||
|
|
@ -646,7 +646,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
else
|
||||
{
|
||||
if (!Shiny.IsValid(pkm)) return false;
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (!IsMatchEggLocation(pkm)) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
}
|
||||
|
||||
|
|
@ -656,7 +656,7 @@ public override bool IsMatchExact(PKM pkm, EvoCriteria evo)
|
|||
if (Nature != -1 && pkm.Nature != Nature) return false;
|
||||
if (Gender != 3 && Gender != pkm.Gender) return false;
|
||||
|
||||
if (pkm is IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
|
||||
if (pkm is PK8 and IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
|
||||
return false;
|
||||
|
||||
if (pkm is not IDynamaxLevel dl || dl.DynamaxLevel < DynamaxLevel)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public sealed class BK4 : G4PKM
|
|||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_4STORED;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
|
||||
public override int Format => 4;
|
||||
public override EntityContext Context => EntityContext.Gen4;
|
||||
public override PersonalInfo PersonalInfo => PersonalTable.HGSS[Species];
|
||||
|
||||
public override byte[] DecryptedBoxData => EncryptedBoxData;
|
||||
|
|
@ -66,7 +66,7 @@ public override uint EXP
|
|||
|
||||
public override int OT_Friendship { get => Data[0x14]; set => Data[0x14] = (byte)value; }
|
||||
public override int Ability { get => Data[0x15]; set => Data[0x15] = (byte)value; }
|
||||
public override int MarkValue { get => Data[0x16]; protected set => Data[0x16] = (byte)value; }
|
||||
public override int MarkValue { get => Data[0x16]; set => Data[0x16] = (byte)value; }
|
||||
public override int Language { get => Data[0x17]; set => Data[0x17] = (byte)value; }
|
||||
public override int EV_HP { get => Data[0x18]; set => Data[0x18] = (byte)value; }
|
||||
public override int EV_ATK { get => Data[0x19]; set => Data[0x19] = (byte)value; }
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public sealed class CK3 : G3PKM, IShadowPKM
|
|||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_3CSTORED;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_3CSTORED;
|
||||
public override int Format => 3;
|
||||
public override EntityContext Context => EntityContext.Gen3;
|
||||
public override PersonalInfo PersonalInfo => PersonalTable.RS[Species];
|
||||
public CK3(byte[] data) : base(data) { }
|
||||
public CK3() : this(new byte[PokeCrypto.SIZE_3CSTORED]) { }
|
||||
|
|
@ -173,7 +173,7 @@ public sealed class CK3 : G3PKM, IShadowPKM
|
|||
public override bool AbilityBit { get => Data[0xCC] == 1; set => Data[0xCC] = value ? (byte)1 : (byte)0; }
|
||||
public override bool Valid { get => Data[0xCD] == 0; set => Data[0xCD] = !value ? (byte)1 : (byte)0; }
|
||||
|
||||
public override int MarkValue { get => SwapBits(Data[0xCF], 1, 2); protected set => Data[0xCF] = (byte)SwapBits(value, 1, 2); }
|
||||
public override int MarkValue { get => SwapBits(Data[0xCF], 1, 2); set => Data[0xCF] = (byte)SwapBits(value, 1, 2); }
|
||||
public override int PKRS_Days { get => Math.Max((sbyte)Data[0xD0], (sbyte)0); set => Data[0xD0] = (byte)(value == 0 ? 0xFF : value & 0xF); }
|
||||
|
||||
public int PartySlot { get => Data[0xD7]; set => Data[0xD7] = (byte)value; } // or not; only really used while in party?
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
|
||||
public abstract class G8PKM : PKM, ISanityChecksum,
|
||||
public abstract class G8PKM : PKM, ISanityChecksum, IMoveReset,
|
||||
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8, IRibbonSetAffixed, ITechRecord8, ISociability,
|
||||
IContestStats, IContestStatsMutable, IHyperTrain, IScaledSize, IGigantamax, IFavorite, IDynamaxLevel, IRibbonIndex, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories
|
||||
{
|
||||
public sealed override int Format => 8;
|
||||
protected G8PKM() : base(PokeCrypto.SIZE_8PARTY) { }
|
||||
protected G8PKM(byte[] data) : base(DecryptParty(data)) { }
|
||||
|
||||
public abstract void ResetMoves();
|
||||
|
||||
private static byte[] DecryptParty(byte[] data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted8(ref data);
|
||||
|
|
@ -124,7 +125,7 @@ public void FixRelearn()
|
|||
public bool Favorite { get => (Data[0x16] & 8) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~8) | ((value ? 1 : 0) << 3)); } // unused, was in LGPE but not in SWSH
|
||||
public bool CanGigantamax { get => (Data[0x16] & 16) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~16) | (value ? 16 : 0)); }
|
||||
// 0x17 alignment unused
|
||||
public override int MarkValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); protected set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); }
|
||||
public override int MarkValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); }
|
||||
// 0x1A alignment unused
|
||||
// 0x1B alignment unused
|
||||
public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x1C), value); }
|
||||
|
|
@ -411,6 +412,8 @@ public void SetPokeJobFlag(int index, bool value)
|
|||
|
||||
public bool GetPokeJobFlagAny() => Array.FindIndex(Data, 0xCE, 14, z => z != 0) >= 0;
|
||||
|
||||
public void ClearPokeJobFlags() => Data.AsSpan(0xCE, 14).Clear();
|
||||
|
||||
public override byte Fullness { get => Data[0xDC]; set => Data[0xDC] = value; }
|
||||
public override byte Enjoyment { get => Data[0xDD]; set => Data[0xDD] = value; }
|
||||
public override int Version { get => Data[0xDE]; set => Data[0xDE] = (byte)value; }
|
||||
|
|
@ -478,6 +481,8 @@ public void SetMoveRecordFlag(int index, bool value)
|
|||
|
||||
public bool GetMoveRecordFlagAny() => Array.FindIndex(Data, 0x127, 14, z => z != 0) >= 0;
|
||||
|
||||
public void ClearMoveRecordFlags() => Data.AsSpan(0x127, 14).Clear();
|
||||
|
||||
// Why did you mis-align this field, GameFreak?
|
||||
public ulong Tracker
|
||||
{
|
||||
|
|
@ -532,5 +537,227 @@ public int GetRibbonByte(int index)
|
|||
index -= 64;
|
||||
return 0x40 + (index >> 3);
|
||||
}
|
||||
|
||||
protected T ConvertTo<T>() where T : G8PKM, new()
|
||||
{
|
||||
var pk = new T();
|
||||
Data.AsSpan().CopyTo(pk.Data);
|
||||
pk.Move1_PPUps = pk.Move2_PPUps = pk.Move3_PPUps = pk.Move4_PPUps = 0;
|
||||
pk.RelearnMove1 = pk.RelearnMove2 = pk.RelearnMove3 = pk.RelearnMove4 = 0;
|
||||
pk.ClearMoveRecordFlags();
|
||||
pk.ClearPokeJobFlags();
|
||||
|
||||
pk.CanGigantamax = false;
|
||||
pk.DynamaxLevel = 0;
|
||||
pk.Sociability = 0;
|
||||
pk.Fullness = 0;
|
||||
pk.Data[0x52] = 0;
|
||||
|
||||
pk.ResetMoves();
|
||||
pk.ResetPartyStats();
|
||||
pk.RefreshChecksum();
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
public virtual PA8 ConvertToPA8()
|
||||
{
|
||||
var pk = new PA8
|
||||
{
|
||||
EncryptionConstant = EncryptionConstant,
|
||||
PID = PID,
|
||||
Species = Species,
|
||||
Form = Form,
|
||||
FormArgument = FormArgument,
|
||||
Gender = Gender,
|
||||
Nature = Nature,
|
||||
StatNature = StatNature,
|
||||
|
||||
TID = TID,
|
||||
SID = SID,
|
||||
EXP = EXP,
|
||||
Ability = Ability,
|
||||
AbilityNumber = AbilityNumber,
|
||||
Language = Language,
|
||||
Version = Version,
|
||||
|
||||
IV_HP = IV_HP,
|
||||
IV_ATK = IV_ATK,
|
||||
IV_DEF = IV_DEF,
|
||||
IV_SPE = IV_SPE,
|
||||
IV_SPA = IV_SPA,
|
||||
IV_SPD = IV_SPD,
|
||||
IsEgg = IsEgg,
|
||||
EV_HP = EV_HP,
|
||||
EV_ATK = EV_ATK,
|
||||
EV_DEF = EV_DEF,
|
||||
EV_SPE = EV_SPE,
|
||||
EV_SPA = EV_SPA,
|
||||
EV_SPD = EV_SPD,
|
||||
|
||||
OT_Gender = OT_Gender,
|
||||
OT_Friendship = OT_Friendship,
|
||||
OT_Intensity = OT_Intensity,
|
||||
OT_Memory = OT_Memory,
|
||||
OT_TextVar = OT_TextVar,
|
||||
OT_Feeling = OT_Feeling,
|
||||
Egg_Year = Egg_Year,
|
||||
Egg_Month = Egg_Month,
|
||||
Egg_Day = Egg_Day,
|
||||
Met_Year = Met_Year,
|
||||
Met_Month = Met_Month,
|
||||
Met_Day = Met_Day,
|
||||
Ball = Ball,
|
||||
Egg_Location = Egg_Location,
|
||||
Met_Location = Met_Location,
|
||||
Met_Level = Met_Level,
|
||||
Tracker = Tracker,
|
||||
|
||||
IsNicknamed = IsNicknamed,
|
||||
CurrentHandler = CurrentHandler,
|
||||
HT_Gender = HT_Gender,
|
||||
HT_Language = HT_Language,
|
||||
HT_Friendship = HT_Friendship,
|
||||
HT_Intensity = HT_Intensity,
|
||||
HT_Memory = HT_Memory,
|
||||
HT_Feeling = HT_Feeling,
|
||||
HT_TextVar = HT_TextVar,
|
||||
|
||||
FatefulEncounter = FatefulEncounter,
|
||||
CNT_Cool = CNT_Cool,
|
||||
CNT_Beauty = CNT_Beauty,
|
||||
CNT_Cute = CNT_Cute,
|
||||
CNT_Smart = CNT_Smart,
|
||||
CNT_Tough = CNT_Tough,
|
||||
CNT_Sheen = CNT_Sheen,
|
||||
|
||||
RibbonChampionKalos = RibbonChampionKalos,
|
||||
RibbonChampionG3 = RibbonChampionG3,
|
||||
RibbonChampionSinnoh = RibbonChampionSinnoh,
|
||||
RibbonBestFriends = RibbonBestFriends,
|
||||
RibbonTraining = RibbonTraining,
|
||||
RibbonBattlerSkillful = RibbonBattlerSkillful,
|
||||
RibbonBattlerExpert = RibbonBattlerExpert,
|
||||
RibbonEffort = RibbonEffort,
|
||||
RibbonAlert = RibbonAlert,
|
||||
RibbonShock = RibbonShock,
|
||||
RibbonDowncast = RibbonDowncast,
|
||||
RibbonCareless = RibbonCareless,
|
||||
RibbonRelax = RibbonRelax,
|
||||
RibbonSnooze = RibbonSnooze,
|
||||
RibbonSmile = RibbonSmile,
|
||||
RibbonGorgeous = RibbonGorgeous,
|
||||
RibbonRoyal = RibbonRoyal,
|
||||
RibbonGorgeousRoyal = RibbonGorgeousRoyal,
|
||||
RibbonArtist = RibbonArtist,
|
||||
RibbonFootprint = RibbonFootprint,
|
||||
RibbonRecord = RibbonRecord,
|
||||
RibbonLegend = RibbonLegend,
|
||||
RibbonCountry = RibbonCountry,
|
||||
RibbonNational = RibbonNational,
|
||||
RibbonEarth = RibbonEarth,
|
||||
RibbonWorld = RibbonWorld,
|
||||
RibbonClassic = RibbonClassic,
|
||||
RibbonPremier = RibbonPremier,
|
||||
RibbonEvent = RibbonEvent,
|
||||
RibbonBirthday = RibbonBirthday,
|
||||
RibbonSpecial = RibbonSpecial,
|
||||
RibbonSouvenir = RibbonSouvenir,
|
||||
RibbonWishing = RibbonWishing,
|
||||
RibbonChampionBattle = RibbonChampionBattle,
|
||||
RibbonChampionRegional = RibbonChampionRegional,
|
||||
RibbonChampionNational = RibbonChampionNational,
|
||||
RibbonChampionWorld = RibbonChampionWorld,
|
||||
HasContestMemoryRibbon = HasContestMemoryRibbon,
|
||||
HasBattleMemoryRibbon = HasBattleMemoryRibbon,
|
||||
RibbonChampionG6Hoenn = RibbonChampionG6Hoenn,
|
||||
RibbonContestStar = RibbonContestStar,
|
||||
RibbonMasterCoolness = RibbonMasterCoolness,
|
||||
RibbonMasterBeauty = RibbonMasterBeauty,
|
||||
RibbonMasterCuteness = RibbonMasterCuteness,
|
||||
RibbonMasterCleverness = RibbonMasterCleverness,
|
||||
RibbonMasterToughness = RibbonMasterToughness,
|
||||
RibbonChampionAlola = RibbonChampionAlola,
|
||||
RibbonBattleRoyale = RibbonBattleRoyale,
|
||||
RibbonBattleTreeGreat = RibbonBattleTreeGreat,
|
||||
RibbonBattleTreeMaster = RibbonBattleTreeMaster,
|
||||
RibbonChampionGalar = RibbonChampionGalar,
|
||||
RibbonTowerMaster = RibbonTowerMaster,
|
||||
RibbonMasterRank = RibbonMasterRank,
|
||||
|
||||
RibbonMarkLunchtime = RibbonMarkLunchtime,
|
||||
RibbonMarkSleepyTime = RibbonMarkSleepyTime,
|
||||
RibbonMarkDusk = RibbonMarkDusk,
|
||||
RibbonMarkDawn = RibbonMarkDawn,
|
||||
RibbonMarkCloudy = RibbonMarkCloudy,
|
||||
RibbonMarkRainy = RibbonMarkRainy,
|
||||
RibbonMarkStormy = RibbonMarkStormy,
|
||||
RibbonMarkSnowy = RibbonMarkSnowy,
|
||||
RibbonMarkBlizzard = RibbonMarkBlizzard,
|
||||
RibbonMarkDry = RibbonMarkDry,
|
||||
RibbonMarkSandstorm = RibbonMarkSandstorm,
|
||||
RibbonCountMemoryContest = RibbonCountMemoryContest,
|
||||
RibbonCountMemoryBattle = RibbonCountMemoryBattle,
|
||||
RibbonMarkMisty = RibbonMarkMisty,
|
||||
RibbonMarkDestiny = RibbonMarkDestiny,
|
||||
RibbonMarkFishing = RibbonMarkFishing,
|
||||
RibbonMarkCurry = RibbonMarkCurry,
|
||||
RibbonMarkUncommon = RibbonMarkUncommon,
|
||||
RibbonMarkRare = RibbonMarkRare,
|
||||
RibbonMarkRowdy = RibbonMarkRowdy,
|
||||
RibbonMarkAbsentMinded = RibbonMarkAbsentMinded,
|
||||
RibbonMarkJittery = RibbonMarkJittery,
|
||||
RibbonMarkExcited = RibbonMarkExcited,
|
||||
RibbonMarkCharismatic = RibbonMarkCharismatic,
|
||||
RibbonMarkCalmness = RibbonMarkCalmness,
|
||||
RibbonMarkIntense = RibbonMarkIntense,
|
||||
RibbonMarkZonedOut = RibbonMarkZonedOut,
|
||||
RibbonMarkJoyful = RibbonMarkJoyful,
|
||||
RibbonMarkAngry = RibbonMarkAngry,
|
||||
RibbonMarkSmiley = RibbonMarkSmiley,
|
||||
RibbonMarkTeary = RibbonMarkTeary,
|
||||
RibbonMarkUpbeat = RibbonMarkUpbeat,
|
||||
RibbonMarkPeeved = RibbonMarkPeeved,
|
||||
RibbonMarkIntellectual = RibbonMarkIntellectual,
|
||||
RibbonMarkFerocious = RibbonMarkFerocious,
|
||||
RibbonMarkCrafty = RibbonMarkCrafty,
|
||||
RibbonMarkScowling = RibbonMarkScowling,
|
||||
RibbonMarkKindly = RibbonMarkKindly,
|
||||
RibbonMarkFlustered = RibbonMarkFlustered,
|
||||
RibbonMarkPumpedUp = RibbonMarkPumpedUp,
|
||||
RibbonMarkZeroEnergy = RibbonMarkZeroEnergy,
|
||||
RibbonMarkPrideful = RibbonMarkPrideful,
|
||||
RibbonMarkUnsure = RibbonMarkUnsure,
|
||||
RibbonMarkHumble = RibbonMarkHumble,
|
||||
RibbonMarkThorny = RibbonMarkThorny,
|
||||
RibbonMarkVigor = RibbonMarkVigor,
|
||||
RibbonMarkSlump = RibbonMarkSlump,
|
||||
RibbonPioneer = RibbonPioneer,
|
||||
RibbonTwinklingStar = RibbonTwinklingStar,
|
||||
|
||||
AffixedRibbon = AffixedRibbon,
|
||||
HyperTrainFlags = HyperTrainFlags,
|
||||
|
||||
BattleVersion = BattleVersion,
|
||||
PKRS_Days = PKRS_Days,
|
||||
PKRS_Strain = PKRS_Strain,
|
||||
HeightScalar = HeightScalar,
|
||||
WeightScalar = WeightScalar,
|
||||
|
||||
Favorite = Favorite,
|
||||
};
|
||||
|
||||
Nickname_Trash.CopyTo(pk.Nickname_Trash);
|
||||
OT_Trash.CopyTo(pk.OT_Trash);
|
||||
HT_Trash.CopyTo(pk.HT_Trash);
|
||||
|
||||
pk.SanitizeImport();
|
||||
|
||||
pk.ResetMoves();
|
||||
pk.ResetPartyStats();
|
||||
pk.RefreshChecksum();
|
||||
|
||||
return pk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user