Remove EvoBase, relocate functions to call sites

Utilize FormInfo to remap battle forms into hatch forms so the encounter matches something valid, and is flagged later in FormVerifier
This commit is contained in:
Kurt 2021-12-26 00:57:40 -08:00
parent 5db5f04066
commit 1d0993f852
6 changed files with 69 additions and 98 deletions

View file

@ -17,10 +17,12 @@ namespace PKHeX.Core
public static IEnumerable<EncounterEgg> GenerateEggs(PKM pkm, IReadOnlyList<EvoCriteria> chain, int generation, bool all = false)
{
System.Diagnostics.Debug.Assert(generation >= 3); // if generating Gen2 eggs, use the other generator.
int species = pkm.Species;
if (!Breeding.CanHatchAsEgg(species))
int currentSpecies = pkm.Species;
if (!Breeding.CanHatchAsEgg(currentSpecies))
yield break;
if (!Breeding.CanHatchAsEgg(species, pkm.Form, generation))
var currentForm = pkm.Form;
if (!Breeding.CanHatchAsEgg(currentSpecies, currentForm, generation))
yield break; // can't originate from eggs
// version is a true indicator for all generation 3-5 origins
@ -31,26 +33,39 @@ namespace PKHeX.Core
int lvl = EggStateLegality.GetEggLevel(generation);
int max = GetMaxSpeciesOrigin(generation);
var e = EvoBase.GetBaseSpecies(chain, 0);
if (e.Species <= max && Breeding.CanHatchAsEgg(e.Species, e.Form, ver))
var (species, form) = GetBaseSpecies(chain, 0);
if ((uint)species <= max)
{
yield return new EncounterEgg(e.Species, e.Form, lvl, generation, ver);
if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver))
yield return new EncounterEgg(e.Species, e.Form, lvl, generation, GetOtherTradePair(ver));
// NOTE: THE SPLIT-BREED SECTION OF CODE SHOULD BE EXACTLY THE SAME AS THE BELOW SECTION
if (FormInfo.IsBattleOnlyForm(species, form, generation))
form = FormInfo.GetOutOfBattleForm(species, form, generation);
if (Breeding.CanHatchAsEgg(species, form, ver))
{
yield return new EncounterEgg(species, form, lvl, generation, ver);
if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver))
yield return new EncounterEgg(species, form, lvl, generation, GetOtherTradePair(ver));
}
}
if (!Breeding.GetSplitBreedGeneration(generation).Contains(species))
if (!Breeding.GetSplitBreedGeneration(generation).Contains(currentSpecies))
yield break; // no other possible species
var o = EvoBase.GetBaseSpecies(chain, 1);
if (o.Species == e.Species)
var otherSplit = species;
(species, form) = GetBaseSpecies(chain, 1);
if ((uint)species == otherSplit)
yield break;
if (o.Species <= max && Breeding.CanHatchAsEgg(o.Species, o.Form, ver))
if (species <= max)
{
yield return new EncounterEgg(o.Species, o.Form, lvl, generation, ver);
if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver))
yield return new EncounterEgg(o.Species, o.Form, lvl, generation, GetOtherTradePair(ver));
// NOTE: THIS SECTION OF CODE SHOULD BE EXACTLY THE SAME AS THE ABOVE SECTION
if (FormInfo.IsBattleOnlyForm(species, form, generation))
form = FormInfo.GetOutOfBattleForm(species, form, generation);
if (Breeding.CanHatchAsEgg(species, form, ver))
{
yield return new EncounterEgg(species, form, lvl, generation, ver);
if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver))
yield return new EncounterEgg(species, form, lvl, generation, GetOtherTradePair(ver));
}
}
}
@ -66,5 +81,19 @@ namespace PKHeX.Core
{
return ver < GameVersion.GP; // lgpe and sw/sh don't have a sister pair
}
private static (int Species, int Form) GetBaseSpecies(IReadOnlyList<EvoCriteria> evolutions, int skipOption)
{
int species = evolutions[0].Species;
if (species == (int)Species.Shedinja) // Shedinja
return ((int)Species.Nincada, 0); // Nincada
// skip n from end, return empty if invalid index
int index = evolutions.Count - 1 - skipOption;
if ((uint)index >= evolutions.Count)
return (-1, 0);
var evo = evolutions[index];
return (evo.Species, evo.Form);
}
}
}

View file

@ -1,21 +1,14 @@
namespace PKHeX.Core
namespace PKHeX.Core;
/// <summary>
/// Small general purpose value passing object with misc data pertaining to an encountered Species.
/// </summary>
public record DexLevel(int Species, int Form) : ISpeciesForm
{
/// <summary>
/// Small general purpose value passing object with misc data pertaining to an encountered Species.
/// Maximum Level
/// </summary>
public class DexLevel
{
public readonly int Species;
public readonly int Form;
public int Level { get; set; }
public int Level { get; set; }
protected DexLevel(int species, int form)
{
Species = species;
Form = form;
}
public override string ToString() => $"{(Species) Species} [{Level}]";
}
}
public override string ToString() => $"{(Species)Species}{(Form == 0 ? "" : $"-{Form}")} [{Level}]";
}

View file

@ -1,47 +0,0 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// Logic for determining the least-evolved species (baby/seed).
/// </summary>
internal static class EvoBase
{
internal static EvoCriteria GetBaseSpecies(PKM pkm, int skipOption = 0)
{
return GetBaseSpecies(pkm, -1, pkm.Format, skipOption);
}
internal static EvoCriteria GetBaseSpecies(PKM pkm, int maxSpeciesOrigin, int generation, int skipOption = 0)
{
int tree = generation;
var table = EvolutionTree.GetEvolutionTree(pkm, tree);
var evos = table.GetValidPreEvolutions(pkm, maxLevel: 100, maxSpeciesOrigin: maxSpeciesOrigin, skipChecks: true);
return GetBaseSpecies(evos, skipOption);
}
private static readonly EvoCriteria Nincada = new(290, 0)
{
Method = (int)EvolutionType.LevelUp,
MinLevel = 20,
Level = 20,
RequiresLvlUp = true,
};
private static readonly EvoCriteria EvoEmpty = new(0, 0)
{
Method = (int)EvolutionType.None,
};
internal static EvoCriteria GetBaseSpecies(IReadOnlyList<EvoCriteria> evolutions, int skipOption = 0)
{
int species = evolutions[0].Species;
if (species == (int)Species.Shedinja) // Shedinja
return Nincada; // Nincada
// skip n from end, return empty if invalid index
int index = evolutions.Count - 1 - skipOption;
return (uint)index >= evolutions.Count ? EvoEmpty : evolutions[index];
}
}
}

View file

@ -1,17 +1,12 @@
namespace PKHeX.Core
namespace PKHeX.Core;
public sealed record EvoCriteria(int Species, int Form) : DexLevel(Species, Form)
{
public sealed class EvoCriteria : DexLevel
{
public EvoCriteria(int species, int form) : base(species, form)
{
}
public int MinLevel { get; set; }
public bool RequiresLvlUp { get; set; }
public int Method { get; init; } = -1;
public int MinLevel { get; set; }
public bool RequiresLvlUp { get; set; }
public int Method { get; init; } = -1;
public bool IsTradeRequired => ((EvolutionType) Method).IsTrade();
public bool IsTradeRequired => ((EvolutionType) Method).IsTrade();
public override string ToString() => $"{(Species) Species}{(Form != 0 ? $"-{Form}" : "")}}} [{MinLevel},{Level}] via {(EvolutionType) Method}";
}
public override string ToString() => $"{(Species) Species}{(Form != 0 ? $"-{Form}" : "")}}} [{MinLevel},{Level}] via {(EvolutionType) Method}";
}

View file

@ -235,7 +235,7 @@ namespace PKHeX.Core
private static int GetRequiredMoveCount(PK1 pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn, IReadOnlyList<int> initialmoves, int originalSpecies)
{
if (SpecialMinMoveSlots.Contains(pk.Species))
return GetRequiredMoveCountSpecial(pk, moves, learn);
return GetRequiredMoveCountSpecial(pk, moves, learn, originalSpecies);
// A pokemon is captured with initial moves and can't forget any until have all 4 slots used
// If it has learn a move before having 4 it will be in one of the free slots
@ -319,12 +319,12 @@ namespace PKHeX.Core
return usedslots;
}
private static int GetRequiredMoveCountSpecial(PKM pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn)
private static int GetRequiredMoveCountSpecial(PKM pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn, int originalSpecies)
{
// Species with few mandatory slots, species with stone evolutions that could evolve at lower level and do not learn any more moves
// and Pikachu and Nidoran family, those only have mandatory the initial moves and a few have one level up moves,
// every other move could be avoided switching game or evolving
var mandatory = GetRequiredMoveCountLevel(pk);
var mandatory = GetRequiredMoveCountLevel(pk, originalSpecies);
switch (pk.Species)
{
case (int)Exeggutor when pk.CurrentLevel >= 28: // Exeggutor
@ -346,10 +346,9 @@ namespace PKHeX.Core
return mandatory.Distinct().Count(z => z != 0) + moves.Where(m => m != 0).Count(m => !mandatory.Contains(m) && learn[1].Contains(m));
}
private static List<int> GetRequiredMoveCountLevel(PKM pk)
private static List<int> GetRequiredMoveCountLevel(PKM pk, int basespecies)
{
int species = pk.Species;
int basespecies = EvoBase.GetBaseSpecies(pk).Species;
int maxlevel = 1;
int minlevel = 1;

View file

@ -107,7 +107,9 @@ namespace PKHeX.Core
if (value == (int)Core.Species.Pikachu && rate == 0xA3) // Light Ball (starter)
return;
int baseSpecies = EvoBase.GetBaseSpecies(this).Species;
var table = EvolutionTree.GetEvolutionTree(1);
var evos = table.GetValidPreEvolutions(this, maxLevel: 100, maxSpeciesOrigin: -1, skipChecks: true);
var baseSpecies = evos[^1].Species;
if (IsCatchRatePreEvolutionRate(baseSpecies, value, rate))
return;