PKHeX/PKHeX.Core/Legality/Encounters/EncounterEgg.cs

121 lines
3.6 KiB
C#
Raw Normal View History

using System;
using System.Linq;
namespace PKHeX.Core
Refactor encounter matching exercise in deferred execution/state machine, only calculate possible matches until a sufficiently valid match is obtained. Previous setup would try to calculate the 'best match' and had band-aid workarounds in cases where a subsequent check may determine it to be a false match. There's still more ways to improve speed: - precalculate relationships for Encounter Slots rather than iterating over every area - yielding individual slots instead of an entire area - group non-egg wondercards by ID in a dict/hashtable for faster retrieval reworked some internals: - EncounterMatch is always an IEncounterable instead of an object, for easy pattern matching. - Splitbreed checking is done per encounter and is stored in the EncounterEgg result - Encounter validation uses Encounter/Move/RelearnMove/Evolution to whittle to the final encounter. As a part of the encounter matching, a lazy peek is used to check if an invalid encounter should be retained instead of discarded; if another encounter has not been checked, it'll stop the invalid checks and move on. If it is the last encounter, no other valid encounters exist so it will keep the parse for the invalid encounter. If no encounters are yielded, then there is no encountermatch. An EncounterInvalid is created to store basic details, and the parse is carried out. Breaks some legality checking features for flagging invalid moves in more detail, but those can be re-added in a separate check (if splitbreed & any move invalid -> check for other split moves). Should now be easier to follow the flow & maintain :smile:
2017-05-28 04:17:53 +00:00
{
/// <summary>
/// Egg Encounter Data
/// </summary>
public class EncounterEgg : IEncounterable, IVersion
Refactor encounter matching exercise in deferred execution/state machine, only calculate possible matches until a sufficiently valid match is obtained. Previous setup would try to calculate the 'best match' and had band-aid workarounds in cases where a subsequent check may determine it to be a false match. There's still more ways to improve speed: - precalculate relationships for Encounter Slots rather than iterating over every area - yielding individual slots instead of an entire area - group non-egg wondercards by ID in a dict/hashtable for faster retrieval reworked some internals: - EncounterMatch is always an IEncounterable instead of an object, for easy pattern matching. - Splitbreed checking is done per encounter and is stored in the EncounterEgg result - Encounter validation uses Encounter/Move/RelearnMove/Evolution to whittle to the final encounter. As a part of the encounter matching, a lazy peek is used to check if an invalid encounter should be retained instead of discarded; if another encounter has not been checked, it'll stop the invalid checks and move on. If it is the last encounter, no other valid encounters exist so it will keep the parse for the invalid encounter. If no encounters are yielded, then there is no encountermatch. An EncounterInvalid is created to store basic details, and the parse is carried out. Breaks some legality checking features for flagging invalid moves in more detail, but those can be re-added in a separate check (if splitbreed & any move invalid -> check for other split moves). Should now be easier to follow the flow & maintain :smile:
2017-05-28 04:17:53 +00:00
{
public int Species { get; set; }
public string Name => "Egg";
public bool EggEncounter => true;
public int LevelMin => Level;
public int LevelMax => Level;
public int Level;
public GameVersion Version { get; set; }
public PKM ConvertToPKM(ITrainerInfo SAV)
{
int gen = Version.GetGeneration();
if (gen < 2)
{
gen = 2;
Version = GameVersion.C;
}
var pk = PKMConverter.GetBlank(gen);
SAV.ApplyToPKM(pk);
pk.Species = Species;
pk.Nickname = PKX.GetSpeciesNameGeneration(Species, SAV.Language, gen);
pk.CurrentLevel = Level;
pk.Version = (int)Version;
int[] moves = GetCurrentEggMoves(pk);
pk.Moves = moves;
pk.SetMaximumPPCurrent(moves);
pk.OT_Friendship = pk.PersonalInfo.BaseFriendship;
pk.SetRandomIVs(flawless: 3);
if (pk.Format <= 2 && Version != GameVersion.C)
return pk;
pk.Met_Level = EncounterSuggestion.GetSuggestedEncounterEggMetLevel(pk);
pk.Met_Location = Math.Max(0, EncounterSuggestion.GetSuggestedEggMetLocation(pk));
if (pk.Format < 3)
return pk;
pk.Ball = 4;
int gender = pk.PersonalInfo.RandomGender;
int nature = Util.Rand.Next(25);
if (pk.Format <= 5)
{
pk.SetPIDGender(gender);
pk.Gender = gender;
pk.SetPIDNature(nature);
pk.Nature = nature;
pk.RefreshAbility(pk.PIDAbility);
}
else
{
pk.PID = Util.Rand32();
pk.Nature = nature;
pk.Gender = gender;
pk.RefreshAbility(Util.Rand.Next(2));
}
bool traded = (int)Version == SAV.Game;
var today = pk.MetDate = DateTime.Today;
if (pk.GenNumber >= 4)
{
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk, traded);
pk.EggMetDate = today;
}
if (pk.Format < 6)
return pk;
if (pk.Gen6)
pk.SetHatchMemory6();
switch (Species)
{
case 774: // Minior
pk.AltForm = Util.Rand.Next(7, 14);
break;
case 664: // Scatterbug
pk.AltForm = Legal.GetVivillonPattern(SAV.Country, SAV.SubRegion);
break;
}
pk.SetRandomEC();
pk.RelearnMoves = moves;
return pk;
}
private int[] GetCurrentEggMoves(PKM pk)
{
var moves = MoveEgg.GetEggMoves(pk, Species, pk.AltForm, Version);
if (moves.Length == 0)
moves = MoveLevelUp.GetEncounterMoves(pk, Level, Version);
else if (moves.Length < 4 && pk.Format >= 6)
{
// Sprinkle in some default level up moves
var lvl = Legal.GetBaseEggMoves(pk, Species, Version, Level);
moves = lvl.Concat(moves).ToArray();
}
return moves;
}
Refactor encounter matching exercise in deferred execution/state machine, only calculate possible matches until a sufficiently valid match is obtained. Previous setup would try to calculate the 'best match' and had band-aid workarounds in cases where a subsequent check may determine it to be a false match. There's still more ways to improve speed: - precalculate relationships for Encounter Slots rather than iterating over every area - yielding individual slots instead of an entire area - group non-egg wondercards by ID in a dict/hashtable for faster retrieval reworked some internals: - EncounterMatch is always an IEncounterable instead of an object, for easy pattern matching. - Splitbreed checking is done per encounter and is stored in the EncounterEgg result - Encounter validation uses Encounter/Move/RelearnMove/Evolution to whittle to the final encounter. As a part of the encounter matching, a lazy peek is used to check if an invalid encounter should be retained instead of discarded; if another encounter has not been checked, it'll stop the invalid checks and move on. If it is the last encounter, no other valid encounters exist so it will keep the parse for the invalid encounter. If no encounters are yielded, then there is no encountermatch. An EncounterInvalid is created to store basic details, and the parse is carried out. Breaks some legality checking features for flagging invalid moves in more detail, but those can be re-added in a separate check (if splitbreed & any move invalid -> check for other split moves). Should now be easier to follow the flow & maintain :smile:
2017-05-28 04:17:53 +00:00
}
public class EncounterEggSplit : EncounterEgg
{
public int OtherSpecies;
}
Refactor encounter matching exercise in deferred execution/state machine, only calculate possible matches until a sufficiently valid match is obtained. Previous setup would try to calculate the 'best match' and had band-aid workarounds in cases where a subsequent check may determine it to be a false match. There's still more ways to improve speed: - precalculate relationships for Encounter Slots rather than iterating over every area - yielding individual slots instead of an entire area - group non-egg wondercards by ID in a dict/hashtable for faster retrieval reworked some internals: - EncounterMatch is always an IEncounterable instead of an object, for easy pattern matching. - Splitbreed checking is done per encounter and is stored in the EncounterEgg result - Encounter validation uses Encounter/Move/RelearnMove/Evolution to whittle to the final encounter. As a part of the encounter matching, a lazy peek is used to check if an invalid encounter should be retained instead of discarded; if another encounter has not been checked, it'll stop the invalid checks and move on. If it is the last encounter, no other valid encounters exist so it will keep the parse for the invalid encounter. If no encounters are yielded, then there is no encountermatch. An EncounterInvalid is created to store basic details, and the parse is carried out. Breaks some legality checking features for flagging invalid moves in more detail, but those can be re-added in a separate check (if splitbreed & any move invalid -> check for other split moves). Should now be easier to follow the flow & maintain :smile:
2017-05-28 04:17:53 +00:00
}