mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 04:23:12 +00:00
More fixes/improvements
Improve cctor times for Breeding; direct calls for splitbreed checks; inlined binary searches via generated IL instead of hashset Fix permitting alpha ribbon on non-alphas in Gen8/8a/8b, disallow Gen8a/8b ribbons for Gen7 Alolan Raichu Improve some IL generation of EvoChain logic Add xmldoc for new evotree additions
This commit is contained in:
parent
79729de90c
commit
6d4cd60461
33 changed files with 300 additions and 249 deletions
|
@ -1,4 +1,3 @@
|
|||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.GameVersion;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
|
@ -13,34 +12,35 @@ public static class Breeding
|
|||
/// Checks if the game has a Daycare, and returns true if it does.
|
||||
/// </summary>
|
||||
/// <param name="game">Version ID to check for.</param>
|
||||
public static bool CanGameGenerateEggs(GameVersion game) => GamesWithEggs.Contains(game);
|
||||
|
||||
private static readonly HashSet<GameVersion> GamesWithEggs = new()
|
||||
public static bool CanGameGenerateEggs(GameVersion game) => game switch
|
||||
{
|
||||
GD, SI, C,
|
||||
R, S, E, FR, LG,
|
||||
D, P, Pt, HG, SS,
|
||||
B, W, B2, W2,
|
||||
X, Y, OR, AS,
|
||||
SN, MN, US, UM,
|
||||
SW, SH, BD, SP,
|
||||
SL, VL,
|
||||
R or S or E or FR or LG => true,
|
||||
D or P or Pt or HG or SS => true,
|
||||
B or W or B2 or W2 => true,
|
||||
X or Y or OR or AS => true,
|
||||
SN or MN or US or UM => true,
|
||||
GD or SI or C => true,
|
||||
SW or SH or BD or SP => true,
|
||||
SL or VL => true,
|
||||
|
||||
GS,
|
||||
GS => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Species that have special handling for breeding.
|
||||
/// </summary>
|
||||
internal static readonly HashSet<ushort> MixedGenderBreeding = new()
|
||||
private static bool IsMixedGenderBreed(ushort species) => species switch
|
||||
{
|
||||
(int)NidoranF,
|
||||
(int)NidoranM,
|
||||
(int)NidoranF => true,
|
||||
(int)NidoranM => true,
|
||||
|
||||
(int)Volbeat,
|
||||
(int)Illumise,
|
||||
(int)Volbeat => true,
|
||||
(int)Illumise => true,
|
||||
|
||||
(int)Indeedee, // male/female
|
||||
(int)Indeedee => true, // male/female
|
||||
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -53,39 +53,48 @@ public static class Breeding
|
|||
var pi = PKX.Personal[species];
|
||||
if (pi is { Genderless: false, OnlyMale: false })
|
||||
return true;
|
||||
if (MixedGenderBreeding.Contains(species))
|
||||
if (IsMixedGenderBreed(species))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly HashSet<ushort> SplitBreed_3 = new()
|
||||
{
|
||||
// Incense
|
||||
(int)Marill,
|
||||
(int)Wobbuffet,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Species that can yield a different baby species when bred.
|
||||
/// </summary>
|
||||
private static readonly HashSet<ushort> SplitBreed = new(SplitBreed_3)
|
||||
public static bool IsSplitBreedNotBabySpecies(ushort species, int generation)
|
||||
{
|
||||
// Incense
|
||||
(int)Chansey,
|
||||
(int)MrMime,
|
||||
(int)Snorlax,
|
||||
(int)Sudowoodo,
|
||||
(int)Mantine,
|
||||
(int)Roselia,
|
||||
(int)Chimecho,
|
||||
};
|
||||
|
||||
internal static IReadOnlySet<ushort>? GetSplitBreedGeneration(int generation) => generation switch
|
||||
{
|
||||
3 => SplitBreed_3,
|
||||
4 or 5 or 6 or 7 or 8 => SplitBreed,
|
||||
if (generation == 3)
|
||||
return IsSplitBreedNotBabySpecies3(species);
|
||||
if (generation is 4 or 5 or 6 or 7 or 8)
|
||||
return IsSplitBreedNotBabySpecies4(species);
|
||||
// Gen9 does not have split-breed egg generation.
|
||||
_ => null,
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the species can yield a different baby species when bred via incense in Generation 3.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a special case for Marill and Wobbuffet, which can be bred with incense to yield Azurill and Wynaut respectively.
|
||||
/// </remarks>
|
||||
public static bool IsSplitBreedNotBabySpecies3(ushort species) => species is (ushort)Marill or (ushort)Wobbuffet;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the species can yield a different baby species when bred via incense in Generation 4-8.
|
||||
/// </summary>
|
||||
public static bool IsSplitBreedNotBabySpecies4(ushort species) => species switch
|
||||
{
|
||||
(int)Marill => true,
|
||||
(int)Wobbuffet => true,
|
||||
|
||||
(int)Chansey => true,
|
||||
(int)MrMime => true,
|
||||
(int)Snorlax => true,
|
||||
(int)Sudowoodo => true,
|
||||
(int)Mantine => true,
|
||||
(int)Roselia => true,
|
||||
(int)Chimecho => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -93,7 +102,7 @@ public static class Breeding
|
|||
/// </summary>
|
||||
/// <remarks>Chained with the other 2 overloads for incremental checks with different parameters.</remarks>
|
||||
/// <param name="species">Current species</param>
|
||||
public static bool CanHatchAsEgg(ushort species) => !NoHatchFromEgg.Contains(species);
|
||||
public static bool CanHatchAsEgg(ushort species) => IsAbleToHatchFromEgg(species);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <see cref="species"/>-<see cref="form"/> can exist as a hatched egg in the requested <see cref="context"/>.
|
||||
|
@ -133,67 +142,69 @@ public static class Breeding
|
|||
/// <summary>
|
||||
/// Species that cannot hatch from an egg.
|
||||
/// </summary>
|
||||
private static readonly HashSet<ushort> NoHatchFromEgg = new()
|
||||
private static bool IsAbleToHatchFromEgg(ushort species) => species switch
|
||||
{
|
||||
// Gen1
|
||||
(int)Ditto,
|
||||
(int)Articuno, (int)Zapdos, (int)Moltres,
|
||||
(int)Mewtwo, (int)Mew,
|
||||
(int)Ditto => false,
|
||||
(int)Articuno or (int)Zapdos or (int)Moltres => false,
|
||||
(int)Mewtwo or (int)Mew => false,
|
||||
|
||||
// Gen2
|
||||
(int)Unown,
|
||||
(int)Raikou, (int)Entei, (int)Suicune,
|
||||
(int)Lugia, (int)HoOh, (int)Celebi,
|
||||
(int)Unown => false,
|
||||
(int)Raikou or (int)Entei or (int)Suicune => false,
|
||||
(int)Lugia or (int)HoOh or (int)Celebi => false,
|
||||
|
||||
// Gen3
|
||||
(int)Regirock, (int)Regice, (int)Registeel,
|
||||
(int)Latias, (int)Latios,
|
||||
(int)Kyogre, (int)Groudon, (int)Rayquaza,
|
||||
(int)Jirachi, (int)Deoxys,
|
||||
(int)Regirock or (int)Regice or (int)Registeel => false,
|
||||
(int)Latias or (int)Latios => false,
|
||||
(int)Kyogre or (int)Groudon or (int)Rayquaza => false,
|
||||
(int)Jirachi or (int)Deoxys => false,
|
||||
|
||||
// Gen4
|
||||
(int)Uxie, (int)Mesprit, (int)Azelf,
|
||||
(int)Dialga, (int)Palkia, (int)Heatran,
|
||||
(int)Regigigas, (int)Giratina, (int)Cresselia,
|
||||
(int)Manaphy, (int)Darkrai, (int)Shaymin, (int)Arceus,
|
||||
(int)Uxie or (int)Mesprit or (int)Azelf => false,
|
||||
(int)Dialga or (int)Palkia or (int)Heatran => false,
|
||||
(int)Regigigas or (int)Giratina or (int)Cresselia => false,
|
||||
(int)Manaphy or (int)Darkrai or (int)Shaymin or (int)Arceus => false,
|
||||
|
||||
// Gen5
|
||||
(int)Victini,
|
||||
(int)Cobalion, (int)Terrakion, (int)Virizion,
|
||||
(int)Tornadus, (int)Thundurus,
|
||||
(int)Reshiram, (int)Zekrom,
|
||||
(int)Landorus, (int)Kyurem,
|
||||
(int)Keldeo, (int)Meloetta, (int)Genesect,
|
||||
(int)Victini => false,
|
||||
(int)Cobalion or (int)Terrakion or (int)Virizion => false,
|
||||
(int)Tornadus or (int)Thundurus => false,
|
||||
(int)Reshiram or (int)Zekrom => false,
|
||||
(int)Landorus or (int)Kyurem => false,
|
||||
(int)Keldeo or (int)Meloetta or (int)Genesect => false,
|
||||
|
||||
// Gen6
|
||||
(int)Xerneas, (int)Yveltal, (int)Zygarde,
|
||||
(int)Diancie, (int)Hoopa, (int)Volcanion,
|
||||
(int)Xerneas or (int)Yveltal or (int)Zygarde => false,
|
||||
(int)Diancie or (int)Hoopa or (int)Volcanion => false,
|
||||
|
||||
// Gen7
|
||||
(int)TypeNull, (int)Silvally,
|
||||
(int)TapuKoko, (int)TapuLele, (int)TapuBulu, (int)TapuFini,
|
||||
(int)Cosmog, (int)Cosmoem, (int)Solgaleo, (int)Lunala,
|
||||
(int)Nihilego, (int)Buzzwole, (int)Pheromosa, (int)Xurkitree, (int)Celesteela, (int)Kartana, (int)Guzzlord, (int)Necrozma,
|
||||
(int)Magearna, (int)Marshadow,
|
||||
(int)Poipole, (int)Naganadel, (int)Stakataka, (int)Blacephalon, (int)Zeraora,
|
||||
(int)TypeNull or (int)Silvally => false,
|
||||
(int)TapuKoko or (int)TapuLele or (int)TapuBulu or (int)TapuFini => false,
|
||||
(int)Cosmog or (int)Cosmoem or (int)Solgaleo or (int)Lunala => false,
|
||||
(int)Nihilego or (int)Buzzwole or (int)Pheromosa or (int)Xurkitree or (int)Celesteela or (int)Kartana or (int)Guzzlord or (int)Necrozma => false,
|
||||
(int)Magearna or (int)Marshadow => false,
|
||||
(int)Poipole or (int)Naganadel or (int)Stakataka or (int)Blacephalon or (int)Zeraora => false,
|
||||
|
||||
(int)Meltan, (int)Melmetal,
|
||||
(int)Meltan or (int)Melmetal => false,
|
||||
|
||||
// Gen8
|
||||
(int)Dracozolt, (int)Arctozolt, (int)Dracovish, (int)Arctovish,
|
||||
(int)Zacian, (int)Zamazenta, (int)Eternatus,
|
||||
(int)Kubfu, (int)Urshifu, (int)Zarude,
|
||||
(int)Regieleki, (int)Regidrago,
|
||||
(int)Glastrier, (int)Spectrier, (int)Calyrex,
|
||||
(int)Enamorus,
|
||||
(int)Dracozolt or (int)Arctozolt or (int)Dracovish or (int)Arctovish => false,
|
||||
(int)Zacian or (int)Zamazenta or (int)Eternatus => false,
|
||||
(int)Kubfu or (int)Urshifu or (int)Zarude => false,
|
||||
(int)Regieleki or (int)Regidrago => false,
|
||||
(int)Glastrier or (int)Spectrier or (int)Calyrex => false,
|
||||
(int)Enamorus => false,
|
||||
|
||||
// Gen9
|
||||
(int)GreatTusk, (int)ScreamTail, (int)BruteBonnet, (int)FlutterMane, (int)SlitherWing, (int)SandyShocks,
|
||||
(int)IronTreads, (int)IronBundle, (int)IronHands, (int)IronJugulis, (int)IronMoth, (int)IronThorns,
|
||||
(int)Gimmighoul, (int)Gholdengo,
|
||||
(int)WoChien, (int)ChienPao, (int)TingLu, (int)ChiYu,
|
||||
(int)RoaringMoon, (int)IronValiant,
|
||||
(int)Koraidon, (int)Miraidon,
|
||||
(int)WalkingWake, (int)IronLeaves,
|
||||
(int)GreatTusk or (int)ScreamTail or (int)BruteBonnet or (int)FlutterMane or (int)SlitherWing or (int)SandyShocks => false,
|
||||
(int)IronTreads or (int)IronBundle or (int)IronHands or (int)IronJugulis or (int)IronMoth or (int)IronThorns => false,
|
||||
(int)Gimmighoul or (int)Gholdengo => false,
|
||||
(int)WoChien or (int)ChienPao or (int)TingLu or (int)ChiYu => false,
|
||||
(int)RoaringMoon or (int)IronValiant => false,
|
||||
(int)Koraidon or (int)Miraidon => false,
|
||||
(int)WalkingWake or (int)IronLeaves => false,
|
||||
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -349,7 +349,7 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies3(devolved.Species))
|
||||
yield break; // no split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -372,10 +372,7 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies3(devolved.Species))
|
||||
yield break;
|
||||
|
||||
species = devolved.Species;
|
||||
|
@ -383,12 +380,6 @@ public sealed class EncounterGenerator3 : IEncounterGenerator
|
|||
yield return CreateEggEncounter(species, form, version);
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static EncounterEgg CreateEggEncounter(ushort species, byte form, GameVersion version)
|
||||
{
|
||||
if (FormInfo.IsBattleOnlyForm(species, form, Generation) || species is (int)Species.Castform)
|
||||
|
|
|
@ -345,7 +345,7 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break; // not a split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -368,10 +368,7 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break;
|
||||
|
||||
species = devolved.Species;
|
||||
|
@ -379,12 +376,6 @@ public sealed class EncounterGenerator4 : IEncounterGenerator
|
|||
yield return CreateEggEncounter(species, form, version);
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static EncounterEgg CreateEggEncounter(ushort species, byte form, GameVersion version)
|
||||
{
|
||||
if (FormInfo.IsBattleOnlyForm(species, form, Generation) || species is (int)Species.Rotom or (int)Species.Castform)
|
||||
|
|
|
@ -276,7 +276,7 @@ public sealed class EncounterGenerator5 : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break; // not a split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -300,10 +300,7 @@ public sealed class EncounterGenerator5 : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break;
|
||||
|
||||
species = devolved.Species;
|
||||
|
@ -311,12 +308,6 @@ public sealed class EncounterGenerator5 : IEncounterGenerator
|
|||
yield return CreateEggEncounter(species, form, version);
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static EncounterEgg CreateEggEncounter(ushort species, byte form, GameVersion version)
|
||||
{
|
||||
if (FormInfo.IsBattleOnlyForm(species, form, Generation) || species is (int)Species.Rotom or (int)Species.Castform)
|
||||
|
|
|
@ -332,7 +332,7 @@ public sealed class EncounterGenerator6 : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break; // not a split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -360,10 +360,7 @@ public sealed class EncounterGenerator6 : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break;
|
||||
|
||||
species = devolved.Species;
|
||||
|
@ -374,12 +371,6 @@ public sealed class EncounterGenerator6 : IEncounterGenerator
|
|||
yield return egg with { Version = GetOtherGamePair(version) };
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static GameVersion GetOtherGamePair(GameVersion version)
|
||||
{
|
||||
// 24 -> 26 ( X -> AS)
|
||||
|
|
|
@ -318,7 +318,7 @@ public sealed class EncounterGenerator7 : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break; // not a split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -346,10 +346,7 @@ public sealed class EncounterGenerator7 : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break;
|
||||
|
||||
species = devolved.Species;
|
||||
|
@ -360,12 +357,6 @@ public sealed class EncounterGenerator7 : IEncounterGenerator
|
|||
yield return egg with { Version = GetOtherGamePair(version) };
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static GameVersion GetOtherGamePair(GameVersion version)
|
||||
{
|
||||
// 30 -> 32 (SN -> US)
|
||||
|
|
|
@ -258,7 +258,7 @@ public sealed class EncounterGenerator8 : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break; // not a split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -280,21 +280,12 @@ public sealed class EncounterGenerator8 : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break;
|
||||
|
||||
yield return CreateEggEncounter(devolved.Species, devolved.Form, version);
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static EncounterEgg CreateEggEncounter(ushort species, byte form, GameVersion version)
|
||||
{
|
||||
if (FormInfo.IsBattleOnlyForm(species, form, Generation) || species is (int)Species.Rotom or (int)Species.Castform)
|
||||
|
|
|
@ -394,7 +394,7 @@ public sealed class EncounterGenerator8b : IEncounterGenerator
|
|||
|
||||
// Ensure most devolved species is the same as the egg species.
|
||||
var (species, form) = GetBaby(devolved);
|
||||
if (species != devolved.Species && !IsValidBabySpecies(devolved.Species))
|
||||
if (species != devolved.Species && !Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break; // not a split-breed.
|
||||
|
||||
// Sanity Check 1
|
||||
|
@ -416,21 +416,12 @@ public sealed class EncounterGenerator8b : IEncounterGenerator
|
|||
yield break; // no split-breed
|
||||
devolved = chain[^2];
|
||||
}
|
||||
var splitSet = Breeding.GetSplitBreedGeneration(Generation);
|
||||
if (splitSet is null)
|
||||
yield break; // Shouldn't happen.
|
||||
if (!splitSet.Contains(devolved.Species))
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies4(devolved.Species))
|
||||
yield break;
|
||||
|
||||
yield return CreateEggEncounter(devolved.Species, devolved.Form, version);
|
||||
}
|
||||
|
||||
private static bool IsValidBabySpecies(ushort species)
|
||||
{
|
||||
var split = Breeding.GetSplitBreedGeneration(Generation);
|
||||
return split is not null && split.Contains(species);
|
||||
}
|
||||
|
||||
private static EncounterEgg CreateEggEncounter(ushort species, byte form, GameVersion version)
|
||||
{
|
||||
if (FormInfo.IsBattleOnlyForm(species, form, Generation) || species is (int)Species.Rotom or (int)Species.Castform)
|
||||
|
|
|
@ -7,6 +7,11 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public static class EvolutionChain
|
||||
{
|
||||
/// <summary>
|
||||
/// Build an <see cref="EvolutionHistory"/> for the given <paramref name="pk"/> and <paramref name="enc"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to search for.</param>
|
||||
/// <param name="enc">Evolution details.</param>
|
||||
public static EvolutionHistory GetEvolutionChainsAllGens(PKM pk, IEncounterTemplate enc)
|
||||
{
|
||||
var min = GetMinLevel(pk, enc);
|
||||
|
@ -17,6 +22,19 @@ public static class EvolutionChain
|
|||
return GetEvolutionChainsSearch(pk, origin, pk.Context, enc.Species);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build an <see cref="EvolutionHistory"/> for the given <paramref name="pk"/> and <paramref name="enc"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to search for.</param>
|
||||
/// <param name="enc">Evolution details.</param>
|
||||
/// <param name="context">Starting (original) context of the <paramref name="pk"/>.</param>
|
||||
/// <param name="encSpecies">Encountered as species. If not known (search for all), set to 0.</param>
|
||||
public static EvolutionHistory GetEvolutionChainsSearch(PKM pk, EvolutionOrigin enc, EntityContext context, ushort encSpecies = 0)
|
||||
{
|
||||
Span<EvoCriteria> chain = stackalloc EvoCriteria[EvolutionTree.MaxEvolutions];
|
||||
return EvolutionChainsSearch(pk, enc, context, encSpecies, chain);
|
||||
}
|
||||
|
||||
private static byte GetMinLevel(PKM pk, IEncounterTemplate enc) => enc.Generation switch
|
||||
{
|
||||
2 => pk is ICaughtData2 c2 ? Math.Max((byte)c2.Met_Level, enc.LevelMin) : enc.LevelMin,
|
||||
|
@ -24,12 +42,6 @@ public static class EvolutionChain
|
|||
_ => Math.Max((byte)pk.Met_Level, enc.LevelMin),
|
||||
};
|
||||
|
||||
public static EvolutionHistory GetEvolutionChainsSearch(PKM pk, EvolutionOrigin enc, EntityContext context, ushort encSpecies)
|
||||
{
|
||||
Span<EvoCriteria> chain = stackalloc EvoCriteria[EvolutionTree.MaxEvolutions];
|
||||
return EvolutionChainsSearch(pk, enc, context, encSpecies, chain);
|
||||
}
|
||||
|
||||
private static EvolutionHistory EvolutionChainsSearch(PKM pk, EvolutionOrigin enc, EntityContext context, ushort encSpecies, Span<EvoCriteria> chain)
|
||||
{
|
||||
var history = new EvolutionHistory();
|
||||
|
@ -63,6 +75,13 @@ public static class EvolutionChain
|
|||
return history;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="EvoCriteria"/> that represent the possible original states of the <paramref name="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="pk">Entity to search for.</param>
|
||||
/// <param name="enc">Evolution details.</param>
|
||||
/// <param name="encSpecies">Encountered as species. If not known (search for all), set to 0.</param>
|
||||
/// <param name="discard">Discard evolutions that are not possible for the original context. Pass false to keep all evolutions.</param>
|
||||
public static EvoCriteria[] GetOriginChain(PKM pk, EvolutionOrigin enc, ushort encSpecies = 0, bool discard = true)
|
||||
{
|
||||
Span<EvoCriteria> result = stackalloc EvoCriteria[EvolutionTree.MaxEvolutions];
|
||||
|
@ -74,6 +93,15 @@ public static class EvolutionChain
|
|||
return chain.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of <see cref="EvoCriteria"/> that represent the possible original states of the <paramref name="pk"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">Span to write results to.</param>
|
||||
/// <param name="pk">Entity to search for.</param>
|
||||
/// <param name="enc">Evolution details.</param>
|
||||
/// <param name="encSpecies">Encountered as species. If not known (search for all), set to 0.</param>
|
||||
/// <param name="discard">Discard evolutions that are not possible for the original context. Pass false to keep all evolutions.</param>
|
||||
/// <returns>Number of valid evolutions found.</returns>
|
||||
public static int GetOriginChain(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc, ushort encSpecies = 0, bool discard = true)
|
||||
{
|
||||
ushort species = enc.Species;
|
||||
|
@ -111,11 +139,13 @@ public static class EvolutionChain
|
|||
return GetCount(result);
|
||||
}
|
||||
|
||||
private static int GetCount(Span<EvoCriteria> result)
|
||||
/// <summary>
|
||||
/// Gets the count of entries that are not empty (species == 0).
|
||||
/// </summary>
|
||||
private static int GetCount(in ReadOnlySpan<EvoCriteria> result)
|
||||
{
|
||||
// return the count of species != 0
|
||||
int count = 0;
|
||||
foreach (var evo in result)
|
||||
foreach (ref readonly var evo in result)
|
||||
{
|
||||
if (evo.Species == 0)
|
||||
break;
|
||||
|
|
|
@ -35,6 +35,8 @@ public sealed class EvolutionGroupHOME : IEvolutionGroup
|
|||
Discard(result, PersonalTable.BDSP);
|
||||
else if (pk.SWSH)
|
||||
Discard(result, PersonalTable.SWSH);
|
||||
else if (pk.GO && result.Length >= 2 && result[1].Species == (ushort)Species.Gimmighoul)
|
||||
result[1] = result[1] with { Form = 1 }; // Roaming, exclusive GO form
|
||||
// GO can be otherwise, don't discard any.
|
||||
}
|
||||
|
||||
|
@ -128,6 +130,14 @@ public sealed class EvolutionGroupHOME : IEvolutionGroup
|
|||
history.Gen8b = SetHistory(result, PersonalTable.BDSP);
|
||||
history.Gen9 = SetHistory(result, PersonalTable.SV);
|
||||
|
||||
if (history.HasVisitedGen7)
|
||||
{
|
||||
// 0->X Alolan forms can't evolve after Gen7 (yet).
|
||||
if (pk is { Species: (int)Species.Raichu, Form: 1 })
|
||||
history.Gen8b = history.Gen8a = Array.Empty<EvoCriteria>();
|
||||
// All others can't enter otherwise (not in game).
|
||||
}
|
||||
|
||||
return present;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@ public sealed class EvolutionHistory
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(context), context, null),
|
||||
};
|
||||
|
||||
public static bool HasVisited(ReadOnlySpan<EvoCriteria> evos, ushort species)
|
||||
public static bool HasVisited(in ReadOnlySpan<EvoCriteria> evos, ushort species)
|
||||
{
|
||||
foreach (var evo in evos)
|
||||
foreach (ref readonly var evo in evos)
|
||||
{
|
||||
if (evo.Species == species)
|
||||
return true;
|
||||
|
|
|
@ -42,6 +42,11 @@ public sealed class EvolutionTree : EvolutionNetwork
|
|||
return new EvolutionTree(forward, reverse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="EvolutionTree"/> for the given <see cref="EntityContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">Context to get the <see cref="EvolutionTree"/> for.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static EvolutionTree GetEvolutionTree(EntityContext context) => context switch
|
||||
{
|
||||
EntityContext.Gen1 => Evolves1,
|
||||
|
|
|
@ -3,6 +3,9 @@ using System.Collections.Generic;
|
|||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides forward evolution pathways with reliance on personal table data for form branched evolutions.
|
||||
/// </summary>
|
||||
public sealed class EvolutionForwardPersonal : IEvolutionForward
|
||||
{
|
||||
private readonly IPersonalTable Personal;
|
||||
|
|
|
@ -3,6 +3,9 @@ using System.Collections.Generic;
|
|||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides forward evolution pathways based only on species.
|
||||
/// </summary>
|
||||
public sealed class EvolutionForwardSpecies : IEvolutionForward
|
||||
{
|
||||
private readonly EvolutionMethod[][] Entries;
|
||||
|
|
|
@ -3,8 +3,14 @@ using System.Collections.Generic;
|
|||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Object storing forward paths for evolution nodes.
|
||||
/// </summary>
|
||||
public interface IEvolutionForward
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the forward evolution paths for the given species and form.
|
||||
/// </summary>
|
||||
ReadOnlyMemory<EvolutionMethod> GetForward(ushort species, byte form);
|
||||
|
||||
/// <summary>
|
||||
|
@ -15,5 +21,16 @@ public interface IEvolutionForward
|
|||
/// <returns>Enumerable of species IDs (with the Form IDs included, left shifted by 11).</returns>
|
||||
IEnumerable<(ushort Species, byte Form)> GetEvolutions(ushort species, byte form);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to evolve the given <see cref="pk"/> to the next evolution stage.
|
||||
/// </summary>
|
||||
/// <param name="head">Current species and form to try evolving</param>
|
||||
/// <param name="next">Expected species and form after evolution</param>
|
||||
/// <param name="pk">Entity to evolve</param>
|
||||
/// <param name="currentMaxLevel">Maximum allowed level for the result</param>
|
||||
/// <param name="levelMin">Minimum level for the result</param>
|
||||
/// <param name="skipChecks">Skip evolution exclusion checks</param>
|
||||
/// <param name="result">Resulting evolution criteria</param>
|
||||
/// <returns>True if the evolution is possible and <see cref="result"/> is valid.</returns>
|
||||
bool TryEvolve(ISpeciesForm head, ISpeciesForm next, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes abstractions for reverse and forward evolution lookups.
|
||||
/// </summary>
|
||||
public interface IEvolutionNetwork
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides interactions to look forward in an evolution tree.
|
||||
/// </summary>
|
||||
IEvolutionForward Forward { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides interactions to look backward in an evolution tree.
|
||||
/// </summary>
|
||||
IEvolutionReverse Reverse { get; }
|
||||
}
|
||||
|
||||
|
@ -38,7 +47,10 @@ public abstract class EvolutionNetwork : IEvolutionNetwork
|
|||
yield return s;
|
||||
}
|
||||
|
||||
public bool IsSpeciesDerivedFrom(ushort species, byte form, int otherSpecies, int otherForm, bool ignoreForm = true)
|
||||
/// <summary>
|
||||
/// Checks if the requested <see cref="species"/>-<see cref="form"/> is can provide any common evolutions with the <see cref="otherSpecies"/>-<see cref="otherForm"/>.
|
||||
/// </summary>
|
||||
public bool IsSpeciesDerivedFrom(ushort species, byte form, ushort otherSpecies, byte otherForm, bool ignoreForm = true)
|
||||
{
|
||||
var evos = GetEvolutionsAndPreEvolutions(species, form);
|
||||
foreach (var (s, f) in evos)
|
||||
|
@ -52,6 +64,9 @@ public abstract class EvolutionNetwork : IEvolutionNetwork
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base (baby) species and form of the given <see cref="species"/>-<see cref="form"/> pair.
|
||||
/// </summary>
|
||||
public (ushort Species, byte Form) GetBaseSpeciesForm(ushort species, byte form)
|
||||
{
|
||||
var chain = Reverse.GetPreEvolutions(species, form);
|
||||
|
@ -59,10 +74,4 @@ public abstract class EvolutionNetwork : IEvolutionNetwork
|
|||
return evo;
|
||||
return (species, form);
|
||||
}
|
||||
|
||||
public int Devolve(Span<EvoCriteria> result, ushort species, byte form, PKM pk, byte levelMin, byte levelMax, ushort stopSpecies,
|
||||
bool skipChecks)
|
||||
{
|
||||
return Reverse.Devolve(result, species, form, pk, levelMin, levelMax, stopSpecies, skipChecks);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ public struct EvolutionNode
|
|||
{
|
||||
/// <summary> First reverse evolution in the node. </summary>
|
||||
public EvolutionLink First;
|
||||
|
||||
/// <summary> Second reverse evolution in the node. Often empty. </summary>
|
||||
public EvolutionLink Second;
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public static class EvolutionReversal
|
|||
int ctr = 1; // count in the 'evos' span
|
||||
while (head.Species != stopSpecies)
|
||||
{
|
||||
var node = lineage[head.Species, head.Form];
|
||||
ref readonly var node = ref lineage[head.Species, head.Form];
|
||||
if (!node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, out var x))
|
||||
return ctr;
|
||||
|
||||
|
@ -52,16 +52,30 @@ public static class EvolutionReversal
|
|||
{
|
||||
// Multiple methods can exist to devolve to the same species-form.
|
||||
// The first method is less restrictive (no LevelUp req), if two {level/other} methods exist.
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
ref var link = ref i == 0 ? ref node.First : ref node.Second;
|
||||
ref readonly var link = ref node.First;
|
||||
if (link.IsEmpty)
|
||||
break;
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var chk = link.Method.Check(pk, currentMaxLevel, levelMin, skipChecks);
|
||||
if (chk != EvolutionCheckResult.Valid)
|
||||
continue;
|
||||
if (chk == EvolutionCheckResult.Valid)
|
||||
{
|
||||
result = Create(link, currentMaxLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
link = ref node.Second;
|
||||
if (link.IsEmpty)
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
chk = link.Method.Check(pk, currentMaxLevel, levelMin, skipChecks);
|
||||
if (chk == EvolutionCheckResult.Valid)
|
||||
{
|
||||
result = Create(link, currentMaxLevel);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@ public sealed class EvolutionReverseLookup : IEvolutionLookup
|
|||
MaxSpecies = maxSpecies;
|
||||
}
|
||||
|
||||
private void Register(EvolutionLink link, ushort species)
|
||||
private void Register(EvolutionLink link, int index)
|
||||
{
|
||||
ref var node = ref Nodes[species];
|
||||
ref var node = ref Nodes[index];
|
||||
node.Add(link);
|
||||
}
|
||||
|
||||
|
@ -32,15 +32,19 @@ public sealed class EvolutionReverseLookup : IEvolutionLookup
|
|||
return;
|
||||
}
|
||||
|
||||
int key = GetKey(species, form);
|
||||
if (!KeyLookup.TryGetValue(key, out var index))
|
||||
{
|
||||
index = Nodes.Length - KeyLookup.Count - 1;
|
||||
KeyLookup.Add(key, index);
|
||||
int index = GetOrAppendIndex(species, form);
|
||||
Register(link, index);
|
||||
}
|
||||
|
||||
ref var node = ref Nodes[index];
|
||||
node.Add(link);
|
||||
private int GetOrAppendIndex(ushort species, byte form)
|
||||
{
|
||||
int key = GetKey(species, form);
|
||||
if (KeyLookup.TryGetValue(key, out var index))
|
||||
return index;
|
||||
|
||||
index = Nodes.Length - KeyLookup.Count - 1;
|
||||
KeyLookup.Add(key, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
private int GetIndex(ushort species, byte form)
|
||||
|
@ -54,10 +58,5 @@ public sealed class EvolutionReverseLookup : IEvolutionLookup
|
|||
}
|
||||
|
||||
private static int GetKey(ushort species, byte form) => species | form << 11;
|
||||
public ref EvolutionNode this[ushort species, byte form] => ref Nodes[GetIndex(species, form)];
|
||||
}
|
||||
|
||||
public interface IEvolutionLookup
|
||||
{
|
||||
ref EvolutionNode this[ushort species, byte form] { get; }
|
||||
public ref readonly EvolutionNode this[ushort species, byte form] => ref Nodes[GetIndex(species, form)];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -6,11 +5,8 @@ namespace PKHeX.Core;
|
|||
public sealed class EvolutionReversePersonal : IEvolutionReverse
|
||||
{
|
||||
public IEvolutionLookup Lineage { get; }
|
||||
|
||||
public EvolutionReversePersonal(EvolutionMethod[][] entries, IPersonalTable t)
|
||||
{
|
||||
Lineage = GetLineage(t, entries);
|
||||
}
|
||||
public EvolutionReversePersonal(EvolutionMethod[][] entries, IPersonalTable t) => Lineage = GetLineage(t, entries);
|
||||
public ref readonly EvolutionNode GetReverse(ushort species, byte form) => ref Lineage[species, form];
|
||||
|
||||
private static EvolutionReverseLookup GetLineage(IPersonalTable t, EvolutionMethod[][] entries)
|
||||
{
|
||||
|
@ -37,8 +33,6 @@ public sealed class EvolutionReversePersonal : IEvolutionReverse
|
|||
return lineage;
|
||||
}
|
||||
|
||||
public EvolutionNode GetReverse(ushort species, byte form) => Lineage[species, form];
|
||||
|
||||
public IEnumerable<(ushort Species, byte Form)> GetPreEvolutions(ushort species, byte form)
|
||||
{
|
||||
var node = Lineage[species, form];
|
||||
|
@ -54,15 +48,9 @@ public sealed class EvolutionReversePersonal : IEvolutionReverse
|
|||
yield return (s.Species, s.Form);
|
||||
}
|
||||
|
||||
public int Devolve(Span<EvoCriteria> result, ushort species, byte form, PKM pk, byte levelMin, byte levelMax, ushort stopSpecies,
|
||||
bool skipChecks)
|
||||
{
|
||||
return Lineage.Devolve(result, species, form, pk, levelMin, levelMax, stopSpecies, skipChecks);
|
||||
}
|
||||
|
||||
public bool TryDevolve(ISpeciesForm head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result)
|
||||
{
|
||||
var node = Lineage[head.Species, head.Form];
|
||||
ref readonly var node = ref Lineage[head.Species, head.Form];
|
||||
return node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, out result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -6,11 +5,8 @@ namespace PKHeX.Core;
|
|||
public sealed class EvolutionReverseSpecies : IEvolutionReverse
|
||||
{
|
||||
public EvolutionReverseLookup Lineage { get; }
|
||||
|
||||
public EvolutionReverseSpecies(EvolutionMethod[][] entries, IPersonalTable t)
|
||||
{
|
||||
Lineage = GetLineage(t, entries);
|
||||
}
|
||||
public EvolutionReverseSpecies(EvolutionMethod[][] entries, IPersonalTable t) => Lineage = GetLineage(t, entries);
|
||||
public ref readonly EvolutionNode GetReverse(ushort species, byte form) => ref Lineage[species, form];
|
||||
|
||||
private static EvolutionReverseLookup GetLineage(IPersonalTable t, EvolutionMethod[][] entries)
|
||||
{
|
||||
|
@ -39,8 +35,6 @@ public sealed class EvolutionReverseSpecies : IEvolutionReverse
|
|||
return lineage;
|
||||
}
|
||||
|
||||
public EvolutionNode GetReverse(ushort species, byte form) => Lineage[species, form];
|
||||
|
||||
public IEnumerable<(ushort Species, byte Form)> GetPreEvolutions(ushort species, byte form)
|
||||
{
|
||||
var node = Lineage[species, form];
|
||||
|
@ -56,15 +50,9 @@ public sealed class EvolutionReverseSpecies : IEvolutionReverse
|
|||
yield return (s.Species, s.Form);
|
||||
}
|
||||
|
||||
public int Devolve(Span<EvoCriteria> result, ushort species, byte form, PKM pk, byte levelMin, byte levelMax, ushort stopSpecies,
|
||||
bool skipChecks)
|
||||
{
|
||||
return Lineage.Devolve(result, species, form, pk, levelMin, levelMax, stopSpecies, skipChecks);
|
||||
}
|
||||
|
||||
public bool TryDevolve(ISpeciesForm head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result)
|
||||
{
|
||||
var node = Lineage[head.Species, head.Form];
|
||||
ref readonly var node = ref Lineage[head.Species, head.Form];
|
||||
return node.TryDevolve(pk, currentMaxLevel, levelMin, skipChecks, out result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public interface IEvolutionLookup
|
||||
{
|
||||
ref readonly EvolutionNode this[ushort species, byte form] { get; }
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Object storing reversal paths for evolution nodes.
|
||||
/// </summary>
|
||||
public interface IEvolutionReverse
|
||||
{
|
||||
EvolutionNode GetReverse(ushort species, byte form);
|
||||
/// <summary>
|
||||
/// Gets the reverse evolution pathways for the given species and form.
|
||||
/// </summary>
|
||||
ref readonly EvolutionNode GetReverse(ushort species, byte form);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all species the <see cref="species"/>-<see cref="form"/> can evolve from, yielded in order of increasing evolution stage.
|
||||
|
@ -15,7 +20,15 @@ public interface IEvolutionReverse
|
|||
/// <returns>Enumerable of species IDs (with the Form IDs included, left shifted by 11).</returns>
|
||||
IEnumerable<(ushort Species, byte Form)> GetPreEvolutions(ushort species, byte form);
|
||||
|
||||
int Devolve(Span<EvoCriteria> result, ushort species, byte form, PKM pk, byte levelMin, byte levelMax, ushort stopSpecies, bool skipChecks);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to devolve the given <see cref="pk"/> to the next evolution stage.
|
||||
/// </summary>
|
||||
/// <param name="head">Current species and form to try devolving</param>
|
||||
/// <param name="pk">Entity to devolve</param>
|
||||
/// <param name="currentMaxLevel">Maximum allowed level for the result</param>
|
||||
/// <param name="levelMin">Minimum level for the result</param>
|
||||
/// <param name="skipChecks">Skip evolution exclusion checks</param>
|
||||
/// <param name="result">Resulting evolution criteria</param>
|
||||
/// <returns>True if the de-evolution is possible and <see cref="result"/> is valid.</returns>
|
||||
bool TryDevolve(ISpeciesForm head, PKM pk, byte currentMaxLevel, byte levelMin, bool skipChecks, out EvoCriteria result);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public sealed class BaseLegalityFormatter : ILegalityFormatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a minimal report string for the analysis.
|
||||
/// </summary>
|
||||
public string GetReport(LegalityAnalysis l)
|
||||
{
|
||||
if (l.Valid)
|
||||
|
@ -20,6 +23,9 @@ public sealed class BaseLegalityFormatter : ILegalityFormatter
|
|||
return string.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a verbose report string for the analysis.
|
||||
/// </summary>
|
||||
public string GetReportVerbose(LegalityAnalysis l)
|
||||
{
|
||||
if (!l.Parsed)
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace PKHeX.Core;
|
|||
/// <remarks>
|
||||
/// If the Entity knew a move at any point in its history, it can be relearned if the current format can learn it.
|
||||
/// </remarks>
|
||||
public class LearnGroupHOME : ILearnGroup
|
||||
public sealed class LearnGroupHOME : ILearnGroup
|
||||
{
|
||||
public static readonly LearnGroupHOME Instance = new();
|
||||
public ushort MaxMoveID => 0;
|
||||
|
|
|
@ -8,8 +8,16 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public static class LearnGroupUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the <see cref="ILearnGroup"/> for the given <see cref="PKM"/>.
|
||||
/// </summary>
|
||||
public static ILearnGroup GetCurrentGroup(PKM pk) => GetCurrentGroup(pk.Context);
|
||||
|
||||
/// <summary>
|
||||
/// Get the <see cref="ILearnGroup"/> for the given <see cref="EntityContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="context">Context to get the <see cref="ILearnGroup"/> for.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static ILearnGroup GetCurrentGroup(EntityContext context) => context switch
|
||||
{
|
||||
Gen1 => LearnGroup1.Instance,
|
||||
|
|
|
@ -172,8 +172,7 @@ public static class MoveListSuggest
|
|||
|
||||
// Split-breed species like Budew & Roselia may be legal for one, and not the other.
|
||||
// If we're not a split-breed or are already legal, return.
|
||||
var split = Breeding.GetSplitBreedGeneration(generation);
|
||||
if (split?.Contains(enc.Species) != true)
|
||||
if (!Breeding.IsSplitBreedNotBabySpecies(enc.Species, generation))
|
||||
return;
|
||||
|
||||
var tmp = pk.Clone();
|
||||
|
|
|
@ -159,7 +159,7 @@ public sealed class MiscVerifier : Verifier
|
|||
var enc = data.EncounterOriginal;
|
||||
if (pk9 is { HeightScalar: 0, WeightScalar: 0 })
|
||||
{
|
||||
if (enc.Context.Generation() < 9 && enc is not EncounterSlot8GO && !data.Info.EvoChainsAllGens.HasVisitedPLA) // <=Gen8 rerolls height/weight, never zero.
|
||||
if (enc.Context.Generation() < 9 && enc is not EncounterSlotGO && !data.Info.EvoChainsAllGens.HasVisitedPLA) // <=Gen8 rerolls height/weight, never zero.
|
||||
data.AddLine(Get(LStatInvalidHeightWeight, Severity.Invalid, Encounter));
|
||||
else if (CheckHeightWeightOdds(enc) && ParseSettings.ZeroHeightWeight != Severity.Valid)
|
||||
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
|
||||
|
|
|
@ -125,7 +125,7 @@ public static class MarkRules
|
|||
return true;
|
||||
|
||||
// Before HOME 3.0.0, this mark was never set.
|
||||
return pk is PK8 or PB8 or PA8; // Not yet touched HOME 3.0.0
|
||||
return wasAlpha && pk is PK8 or PB8 or PA8; // Not yet touched HOME 3.0.0
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// Side game data for <see cref="PA8"/> data transferred into HOME.
|
||||
/// </summary>
|
||||
public class GameDataPA8 : HomeOptional1, IGameDataSide<PA8>, IScaledSizeAbsolute, IScaledSize3
|
||||
public sealed class GameDataPA8 : HomeOptional1, IGameDataSide<PA8>, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility
|
||||
{
|
||||
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PA8;
|
||||
private const int SIZE = HomeCrypto.SIZE_2GAME_PA8;
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// Side game data for <see cref="PB7"/> data transferred into HOME.
|
||||
/// </summary>
|
||||
public sealed class GameDataPB7 : HomeOptional1, IGameDataSide<PB7>, IScaledSizeAbsolute, IMemoryOT
|
||||
public sealed class GameDataPB7 : HomeOptional1, IGameDataSide<PB7>, IScaledSizeAbsolute, IMemoryOT, IGameDataSplitAbility
|
||||
{
|
||||
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PB7;
|
||||
private const int SIZE = HomeCrypto.SIZE_2GAME_PB7;
|
||||
|
|
|
@ -6,7 +6,7 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// Record Mixing Data for Generation 3 <see cref="SAV3"/> games.
|
||||
/// </summary>
|
||||
public class RecordMixing3Gift
|
||||
public sealed class RecordMixing3Gift
|
||||
{
|
||||
/// <summary>
|
||||
/// 0x8: Total Size of this object
|
||||
|
|
|
@ -12,6 +12,10 @@ public class MarshalTests
|
|||
[InlineData(8, typeof(MoveResult))]
|
||||
[InlineData(8, typeof(EvolutionMethod))]
|
||||
[InlineData(8, typeof(Moveset))]
|
||||
[InlineData(8, typeof(SCXorShift32))]
|
||||
[InlineData(16, typeof(Xoroshiro128Plus))]
|
||||
[InlineData(16, typeof(Xoroshiro128Plus8b))]
|
||||
[InlineData(16, typeof(XorShift128))]
|
||||
public void MarshalSizeExact(int expect, Type t) => Marshal.SizeOf(t).Should().Be(expect);
|
||||
|
||||
[Theory]
|
||||
|
|
Loading…
Reference in a new issue