PKHeX/PKHeX.Core/Editing/Saves/BoxManip/BoxManipDefaults.cs
Kurt 9166d0eb64
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves

In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.

The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.

The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.

* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 16:15:27 -07:00

78 lines
4.7 KiB
C#

using System.Collections.Generic;
using PKHeX.Core.Searching;
using static PKHeX.Core.BoxManipType;
namespace PKHeX.Core;
/// <summary>
/// Curated lists of <see cref="BoxManipBase"/> implementations to present as options to the user.
/// </summary>
public static class BoxManipDefaults
{
/// <summary>
/// Common sorting actions.
/// </summary>
public static readonly IReadOnlyList<BoxManipBase> SortCommon = new List<BoxManipBase>
{
new BoxManipSort(SortSpecies, EntitySorting.OrderBySpecies),
new BoxManipSort(SortSpeciesReverse, EntitySorting.OrderByDescendingSpecies),
new BoxManipSort(SortLevel, EntitySorting.OrderByLevel),
new BoxManipSort(SortLevelReverse, EntitySorting.OrderByDescendingLevel),
new BoxManipSort(SortDate, EntitySorting.OrderByDateObtained, s => s.Generation >= 4),
new BoxManipSort(SortName, list => list.OrderBySpeciesName(GameInfo.Strings.Species)),
new BoxManipSort(SortFavorite, list => list.OrderByCustom(pk => pk is IFavorite {Favorite: true}), s => s.BlankPKM is IFavorite),
new BoxManipSortComplex(SortParty, (list, sav, start) => list.BubbleUp(sav, i => ((SAV7b)sav).Blocks.Storage.IsParty(i), start), s => s is SAV7b),
new BoxManipSort(SortShiny, list => list.OrderByCustom(pk => !pk.IsShiny)),
new BoxManipSort(SortRandom, list => list.OrderByCustom(_ => Util.Rand32())),
};
/// <summary>
/// Advanced sorting actions.
/// </summary>
public static readonly IReadOnlyList<BoxManipBase> SortAdvanced = new List<BoxManipBase>
{
new BoxManipSort(SortUsage, EntitySorting.OrderByUsage, s => s.Generation >= 3),
new BoxManipSort(SortPotential, list => list.OrderByCustom(pk => (pk.MaxIV * 6) - pk.IVTotal)),
new BoxManipSort(SortTraining, list => list.OrderByCustom(pk => (pk.MaxEV * 6) - pk.EVTotal)),
new BoxManipSortComplex(SortOwner, (list, sav) => list.OrderByOwnership(sav)),
new BoxManipSort(SortType, list => list.OrderByCustom(pk => pk.PersonalInfo.Type1, pk => pk.PersonalInfo.Type2)),
new BoxManipSort(SortVersion, list => list.OrderByCustom(pk => pk.Generation, pk => pk.Version, pk => pk.Met_Location), s => s.Generation >= 3),
new BoxManipSort(SortBST, list => list.OrderByCustom(pk => pk.PersonalInfo.GetBaseStatTotal())),
new BoxManipSort(SortCP, list => list.OrderByCustom(pk => pk is PB7 pb7 ? pb7.Stat_CP : 0), s => s is SAV7b),
new BoxManipSort(SortLegal, list => list.OrderByCustom(pk => !new LegalityAnalysis(pk).Valid)),
new BoxManipSort(SortEncounterType, list => list.OrderByCustom(pk => new LegalityAnalysis(pk).Info.EncounterMatch.LongName)),
};
/// <summary>
/// Common deletion actions.
/// </summary>
public static readonly IReadOnlyList<BoxManipBase> ClearCommon = new List<BoxManipBase>
{
new BoxManipClear(DeleteAll, _ => true),
new BoxManipClear(DeleteEggs, pk => pk.IsEgg, s => s.Generation >= 2 && s is not SAV8LA),
new BoxManipClearComplex(DeletePastGen, (pk, sav) => pk.Generation != sav.Generation, s => s.Generation >= 4),
new BoxManipClearComplex(DeleteForeign, (pk, sav) => !sav.IsOriginalHandler(pk, pk.Format > 2)),
new BoxManipClear(DeleteUntrained, pk => pk.EVTotal == 0, s => s is not SAV8LA),
new BoxManipClear(DeleteUntrained, pk => !((PA8)pk).IsGanbaruValuesMax(pk), s => s is SAV8LA),
new BoxManipClear(DeleteItemless, pk => pk.HeldItem == 0, s => s is not SAV8LA),
new BoxManipClear(DeleteIllegal, pk => !new LegalityAnalysis(pk).Valid),
new BoxManipClearDuplicate<string>(DeleteClones, pk => SearchUtil.GetCloneDetectMethod(CloneDetectionMethod.HashDetails)(pk)),
};
/// <summary>
/// Common modifying actions.
/// </summary>
public static readonly IReadOnlyList<BoxManipModify> ModifyCommon = new List<BoxManipModify>
{
new(ModifyHatchEggs, pk => pk.ForceHatchPKM(), s => s.Generation >= 2 && s is not SAV8LA),
new(ModifyMaxFriendship, pk => pk.MaximizeFriendship()),
new(ModifyMaxLevel, pk => pk.MaximizeLevel()),
new(ModifyResetMoves, pk => pk.SetMoves(pk.GetMoveSet()), s => s.Generation >= 3),
new(ModifyRandomMoves, pk => pk.SetMoves(pk.GetMoveSet(true))),
new(ModifyHyperTrain,pk => pk.SetSuggestedHyperTrainingData(), s => s.Generation >= 7 && s is not SAV8LA),
new(ModifyGanbaru,pk => ((IGanbaru)pk).SetSuggestedGanbaruValues(pk), s => s is SAV8LA),
new(ModifyRemoveNicknames, pk => pk.SetDefaultNickname()),
new(ModifyRemoveItem, pk => pk.HeldItem = 0, s => s.Generation >= 2),
new(ModifyHeal, pk => pk.Heal(), s => s.Generation >= 6), // HP stored in box, or official code has bugged transfer PP the user would like to rectify.
};
}