PKHeX/PKHeX.Core/Game/GameStrings/GameDataSource.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

126 lines
4.9 KiB
C#

using System;
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Bundles raw string inputs into lists that can be used in data binding.
/// </summary>
public sealed class GameDataSource
{
public static readonly IReadOnlyList<ComboItem> Regions = new List<ComboItem>
{
new ("Japan (日本)", 0),
new ("Americas (NA/SA)", 1),
new ("Europe (EU/AU)", 2),
new ("China (中国大陆)", 4),
new ("Korea (한국)", 5),
new ("Taiwan (香港/台灣)", 6),
};
private static readonly List<ComboItem> LanguageList = new()
{
new ComboItem("JPN (日本語)", (int)LanguageID.Japanese),
new ComboItem("ENG (English)", (int)LanguageID.English),
new ComboItem("FRE (Français)", (int)LanguageID.French),
new ComboItem("ITA (Italiano)", (int)LanguageID.Italian),
new ComboItem("GER (Deutsch)", (int)LanguageID.German),
new ComboItem("ESP (Español)", (int)LanguageID.Spanish),
new ComboItem("KOR (한국어)", (int)LanguageID.Korean),
new ComboItem("CHS (简体中文)", (int)LanguageID.ChineseS),
new ComboItem("CHT (繁體中文)", (int)LanguageID.ChineseT),
};
public GameDataSource(GameStrings s)
{
Strings = s;
BallDataSource = GetBalls(s.itemlist);
SpeciesDataSource = Util.GetCBList(s.specieslist);
NatureDataSource = Util.GetCBList(s.natures);
AbilityDataSource = Util.GetCBList(s.abilitylist);
GroundTileDataSource = Util.GetUnsortedCBList(s.groundtiletypes, GroundTileTypeExtensions.ValidTileTypes);
var moves = Util.GetCBList(s.movelist);
HaXMoveDataSource = moves;
var legal = new List<ComboItem>(moves);
legal.RemoveAll(m => MoveInfo.Z_Moves.Contains(m.Value));
LegalMoveDataSource = legal;
VersionDataSource = GetVersionList(s);
Met = new MetDataSource(s);
Empty = new ComboItem(s.Species[0], 0);
}
/// <summary> Strings that this object's lists were generated with. </summary>
public readonly GameStrings Strings;
/// <summary> Contains Met Data lists to source lists from. </summary>
public readonly MetDataSource Met;
/// <summary> Represents "(None)", localized to this object's language strings. </summary>
public readonly ComboItem Empty;
public readonly IReadOnlyList<ComboItem> SpeciesDataSource;
public readonly IReadOnlyList<ComboItem> BallDataSource;
public readonly IReadOnlyList<ComboItem> NatureDataSource;
public readonly IReadOnlyList<ComboItem> AbilityDataSource;
public readonly IReadOnlyList<ComboItem> VersionDataSource;
public readonly IReadOnlyList<ComboItem> LegalMoveDataSource;
public readonly IReadOnlyList<ComboItem> HaXMoveDataSource;
public readonly IReadOnlyList<ComboItem> GroundTileDataSource;
private static IReadOnlyList<ComboItem> GetBalls(string[] itemList)
{
// ignores Poke/Great/Ultra
ReadOnlySpan<ushort> ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851, 1785, 1710, 1711, 1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771 };
ReadOnlySpan<byte> ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026, 0027, 0028, 0029, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037 };
return Util.GetVariedCBListBall(itemList, ball_nums, ball_vals);
}
private static IReadOnlyList<ComboItem> GetVersionList(GameStrings s)
{
var list = s.gamelist;
ReadOnlySpan<byte> games = stackalloc byte[]
{
47, // 8 legends arceus
48, 49, // 8 bdsp
44, 45, // 8 swsh
42, 43, // 7 gg
30, 31, // 7 sm
32, 33, // 7 usum
24, 25, // 6 xy
27, 26, // 6 oras
21, 20, // 5 bw
23, 22, // 5 b2w2
10, 11, 12, // 4 dppt
07, 08, // 4 hgss
02, 01, 03, // 3 rse
04, 05, // 3 frlg
15, // 3 cxd
39, 40, 41, // 7vc2
35, 36, 37, 38, // 7vc1
34, // 7go
};
return Util.GetUnsortedCBList(list, games);
}
public List<ComboItem> GetItemDataSource(GameVersion game, EntityContext context, IReadOnlyList<ushort> allowed, bool HaX = false)
{
var items = Strings.GetItemStrings(context, game);
return HaX ? Util.GetCBList(items) : Util.GetCBList(items, allowed);
}
public static IReadOnlyList<ComboItem> LanguageDataSource(int gen)
{
var languages = new List<ComboItem>(LanguageList);
if (gen == 3)
languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean);
else if (gen < 7)
languages.RemoveAll(l => l.Value > (int)LanguageID.Korean);
return languages;
}
}